Merge branch 'develop' of https://github.com/frappe/erpnext into develop
This commit is contained in:
commit
0c4ce4dbba
@ -326,7 +326,7 @@ def make_payment_request(**args):
|
|||||||
"reference_doctype": args.dt,
|
"reference_doctype": args.dt,
|
||||||
"reference_name": args.dn,
|
"reference_name": args.dn,
|
||||||
"party_type": args.get("party_type") or "Customer",
|
"party_type": args.get("party_type") or "Customer",
|
||||||
"party": args.get("party") or ref_doc.customer,
|
"party": args.get("party") or ref_doc.get("customer"),
|
||||||
"bank_account": bank_account
|
"bank_account": bank_account
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -420,7 +420,7 @@ def make_payment_entry(docname):
|
|||||||
|
|
||||||
def update_payment_req_status(doc, method):
|
def update_payment_req_status(doc, method):
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
|
||||||
|
|
||||||
for ref in doc.references:
|
for ref in doc.references:
|
||||||
payment_request_name = frappe.db.get_value("Payment Request",
|
payment_request_name = frappe.db.get_value("Payment Request",
|
||||||
{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
|
{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
|
||||||
@ -430,7 +430,7 @@ def update_payment_req_status(doc, method):
|
|||||||
ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
|
ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
|
||||||
pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
|
pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
|
||||||
status = pay_req_doc.status
|
status = pay_req_doc.status
|
||||||
|
|
||||||
if status != "Paid" and not ref_details.outstanding_amount:
|
if status != "Paid" and not ref_details.outstanding_amount:
|
||||||
status = 'Paid'
|
status = 'Paid'
|
||||||
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
|
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
|
||||||
|
@ -261,12 +261,25 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
price_list: this.frm.doc.buying_price_list
|
price_list: this.frm.doc.buying_price_list
|
||||||
}, function() {
|
}, function() {
|
||||||
me.apply_pricing_rule();
|
me.apply_pricing_rule();
|
||||||
|
|
||||||
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
||||||
|
me.frm.doc.tax_withholding_category = me.frm.supplier_tds;
|
||||||
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apply_tds: function(frm) {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
if (!me.frm.doc.apply_tds) {
|
||||||
|
me.frm.set_value("tax_withholding_category", '');
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", 1);
|
||||||
|
} else {
|
||||||
|
me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
credit_to: function() {
|
credit_to: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
if(this.frm.doc.credit_to) {
|
if(this.frm.doc.credit_to) {
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"supplier_name",
|
"supplier_name",
|
||||||
"tax_id",
|
"tax_id",
|
||||||
"due_date",
|
"due_date",
|
||||||
|
"tax_withholding_category",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"company",
|
"company",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
@ -1294,13 +1295,21 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Internal Supplier",
|
"label": "Is Internal Supplier",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax_withholding_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Tax Withholding Category",
|
||||||
|
"options": "Tax Withholding Category",
|
||||||
|
"print_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-17 13:05:25.199832",
|
"modified": "2020-04-18 13:05:25.199832",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
@ -1002,7 +1002,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.apply_tds:
|
if not self.apply_tds:
|
||||||
return
|
return
|
||||||
|
|
||||||
tax_withholding_details = get_party_tax_withholding_details(self)
|
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||||
|
|
||||||
if not tax_withholding_details:
|
if not tax_withholding_details:
|
||||||
return
|
return
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-05-21 16:16:04",
|
"creation": "2013-05-21 16:16:04",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -14,11 +15,11 @@
|
|||||||
"col_break1",
|
"col_break1",
|
||||||
"account_head",
|
"account_head",
|
||||||
"description",
|
"description",
|
||||||
|
"section_break_10",
|
||||||
|
"rate",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"section_break_10",
|
|
||||||
"rate",
|
|
||||||
"section_break_9",
|
"section_break_9",
|
||||||
"tax_amount",
|
"tax_amount",
|
||||||
"tax_amount_after_discount_amount",
|
"tax_amount_after_discount_amount",
|
||||||
@ -27,8 +28,7 @@
|
|||||||
"base_tax_amount",
|
"base_tax_amount",
|
||||||
"base_total",
|
"base_total",
|
||||||
"base_tax_amount_after_discount_amount",
|
"base_tax_amount_after_discount_amount",
|
||||||
"item_wise_tax_detail",
|
"item_wise_tax_detail"
|
||||||
"parenttype"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -53,6 +53,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
|
"default": "On Net Total",
|
||||||
"fieldname": "charge_type",
|
"fieldname": "charge_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -196,15 +197,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "parenttype",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Parenttype",
|
|
||||||
"oldfieldname": "parenttype",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"print_hide": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "accounting_dimensions_section",
|
"fieldname": "accounting_dimensions_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -217,11 +209,14 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-25 23:08:38.281025",
|
"links": [],
|
||||||
|
"modified": "2020-03-12 14:53:47.679439",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Taxes and Charges",
|
"name": "Purchase Taxes and Charges",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
@ -26,16 +26,24 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
||||||
|
|
||||||
frm.add_custom_button('E-Way Bill JSON', () => {
|
frm.add_custom_button('E-Way Bill JSON', () => {
|
||||||
var w = window.open(
|
frappe.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
'dt': frm.doc.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
'dn': [frm.doc.name]
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
|
data: r.message,
|
||||||
|
docname: frm.doc.name
|
||||||
|
};
|
||||||
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}, __("Create"));
|
}, __("Create"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,17 +16,23 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var w = window.open(
|
frappe.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(doclist.doctype)
|
'dt': doclist.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(docnames)
|
'dn': docnames
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
|
data: r.message,
|
||||||
|
docname: docnames
|
||||||
|
};
|
||||||
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
|
doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
|
||||||
|
@ -587,7 +587,9 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm.set_query("account_for_change_amount", function() {
|
frm.set_query("account_for_change_amount", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
account_type: ['in', ["Cash", "Bank"]]
|
account_type: ['in', ["Cash", "Bank"]],
|
||||||
|
company: frm.doc.company,
|
||||||
|
is_group: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -668,7 +670,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm.fields_dict["loyalty_redemption_account"].get_query = function() {
|
frm.fields_dict["loyalty_redemption_account"].get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters:{
|
filters:{
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -677,7 +680,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() {
|
frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters:{
|
filters:{
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1892,7 +1892,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
data = get_ewb_data("Sales Invoice", si.name)
|
data = get_ewb_data("Sales Invoice", [si.name])
|
||||||
|
|
||||||
self.assertEqual(data['version'], '1.0.1118')
|
self.assertEqual(data['version'], '1.0.1118')
|
||||||
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
||||||
|
@ -6,23 +6,42 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, getdate
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
class TaxWithholdingCategory(Document):
|
class TaxWithholdingCategory(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_party_tax_withholding_details(ref_doc):
|
def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
|
||||||
tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
|
|
||||||
|
pan_no = ''
|
||||||
|
suppliers = []
|
||||||
|
|
||||||
|
if not tax_withholding_category:
|
||||||
|
tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
|
||||||
|
|
||||||
if not tax_withholding_category:
|
if not tax_withholding_category:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not pan_no:
|
||||||
|
pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
|
||||||
|
|
||||||
|
# Get others suppliers with the same PAN No
|
||||||
|
if pan_no:
|
||||||
|
suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
|
||||||
|
|
||||||
|
if not suppliers:
|
||||||
|
suppliers.append(ref_doc.supplier)
|
||||||
|
|
||||||
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
||||||
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
||||||
if not tax_details:
|
if not tax_details:
|
||||||
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
||||||
.format(tax_withholding_category, ref_doc.company))
|
.format(tax_withholding_category, ref_doc.company))
|
||||||
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
|
||||||
|
tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
|
||||||
|
tax_details, fy, ref_doc.posting_date, pan_no)
|
||||||
|
|
||||||
tax_row = get_tax_row(tax_details, tds_amount)
|
tax_row = get_tax_row(tax_details, tds_amount)
|
||||||
|
|
||||||
return tax_row
|
return tax_row
|
||||||
@ -51,6 +70,7 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
|||||||
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
||||||
|
|
||||||
def get_tax_row(tax_details, tds_amount):
|
def get_tax_row(tax_details, tds_amount):
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"category": "Total",
|
"category": "Total",
|
||||||
"add_deduct_tax": "Deduct",
|
"add_deduct_tax": "Deduct",
|
||||||
@ -60,25 +80,36 @@ def get_tax_row(tax_details, tds_amount):
|
|||||||
"tax_amount": tds_amount
|
"tax_amount": tds_amount
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
|
||||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
||||||
tds_amount = 0
|
tds_amount = 0
|
||||||
tds_deducted = 0
|
tds_deducted = 0
|
||||||
|
|
||||||
def _get_tds(amount):
|
def _get_tds(amount, rate):
|
||||||
if amount <= 0:
|
if amount <= 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return amount * tax_details.rate / 100
|
return amount * rate / 100
|
||||||
|
|
||||||
|
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
|
||||||
|
{
|
||||||
|
'pan_no': pan_no,
|
||||||
|
'fiscal_year': fiscal_year
|
||||||
|
}, 'name')
|
||||||
|
ldc = ''
|
||||||
|
|
||||||
|
if ldc_name:
|
||||||
|
ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
|
||||||
|
|
||||||
entries = frappe.db.sql("""
|
entries = frappe.db.sql("""
|
||||||
select voucher_no, credit
|
select voucher_no, credit
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and fiscal_year=%s and credit > 0
|
where company = %s and
|
||||||
""", (ref_doc.supplier, fiscal_year), as_dict=1)
|
party in %s and fiscal_year=%s and credit > 0
|
||||||
|
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
vouchers = [d.voucher_no for d in entries]
|
||||||
advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year)
|
advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
|
||||||
|
|
||||||
tds_vouchers = vouchers + advance_vouchers
|
tds_vouchers = vouchers + advance_vouchers
|
||||||
|
|
||||||
@ -93,7 +124,20 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
|||||||
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
||||||
|
|
||||||
if tds_deducted:
|
if tds_deducted:
|
||||||
tds_amount = _get_tds(ref_doc.net_total)
|
if ldc:
|
||||||
|
limit_consumed = frappe.db.get_value('Purchase Invoice',
|
||||||
|
{
|
||||||
|
'supplier': ('in', suppliers),
|
||||||
|
'apply_tds': 1,
|
||||||
|
'docstatus': 1
|
||||||
|
}, 'sum(net_total)')
|
||||||
|
|
||||||
|
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
|
||||||
|
ldc.certificate_limit):
|
||||||
|
|
||||||
|
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
|
||||||
|
else:
|
||||||
|
tds_amount = _get_tds(net_total, tax_details.rate)
|
||||||
else:
|
else:
|
||||||
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
||||||
fields = ['sum(net_amount)'],
|
fields = ['sum(net_amount)'],
|
||||||
@ -106,43 +150,79 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
|||||||
fields = ['sum(credit_in_account_currency)'],
|
fields = ['sum(credit_in_account_currency)'],
|
||||||
filters = {
|
filters = {
|
||||||
'parent': ('in', vouchers), 'docstatus': 1,
|
'parent': ('in', vouchers), 'docstatus': 1,
|
||||||
'party': ref_doc.supplier,
|
'party': ('in', suppliers),
|
||||||
'reference_type': ('not in', ['Purchase Invoice'])
|
'reference_type': ('not in', ['Purchase Invoice'])
|
||||||
}, as_list=1)
|
}, as_list=1)
|
||||||
|
|
||||||
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
||||||
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
||||||
|
|
||||||
supplier_credit_amount += ref_doc.net_total
|
supplier_credit_amount += net_total
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
|
||||||
supplier_credit_amount -= debit_note_amount
|
supplier_credit_amount -= debit_note_amount
|
||||||
|
|
||||||
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
||||||
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
||||||
tds_amount = _get_tds(supplier_credit_amount)
|
|
||||||
|
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
|
||||||
|
ldc.certificate_limit):
|
||||||
|
tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
|
||||||
|
tax_details)
|
||||||
|
else:
|
||||||
|
tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
|
||||||
|
|
||||||
return tds_amount
|
return tds_amount
|
||||||
|
|
||||||
def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=None, to_date=None):
|
def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
|
||||||
condition = "fiscal_year=%s" % fiscal_year
|
condition = "fiscal_year=%s" % fiscal_year
|
||||||
|
|
||||||
|
if company:
|
||||||
|
condition += "and company =%s" % (company)
|
||||||
if from_date and to_date:
|
if from_date and to_date:
|
||||||
condition = "company=%s and posting_date between %s and %s" % (company, from_date, to_date)
|
condition += "and posting_date between %s and %s" % (company, from_date, to_date)
|
||||||
|
|
||||||
|
## Appending the same supplier again if length of suppliers list is 1
|
||||||
|
## since tuple of single element list contains None, For example ('Test Supplier 1', )
|
||||||
|
## and the below query fails
|
||||||
|
if len(suppliers) == 1:
|
||||||
|
suppliers.append(suppliers[0])
|
||||||
|
|
||||||
return frappe.db.sql_list("""
|
return frappe.db.sql_list("""
|
||||||
select distinct voucher_no
|
select distinct voucher_no
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and %s and debit > 0
|
where party in %s and %s and debit > 0
|
||||||
""", (supplier, condition)) or []
|
""", (tuple(suppliers), condition)) or []
|
||||||
|
|
||||||
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
|
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
||||||
condition = ""
|
condition = "and 1=1"
|
||||||
if company:
|
if company:
|
||||||
condition = " and company=%s " % company
|
condition = " and company=%s " % company
|
||||||
|
|
||||||
|
if len(suppliers) == 1:
|
||||||
|
suppliers.append(suppliers[0])
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
return flt(frappe.db.sql("""
|
||||||
select abs(sum(net_total))
|
select abs(sum(net_total))
|
||||||
from `tabPurchase Invoice`
|
from `tabPurchase Invoice`
|
||||||
where supplier=%s %s and is_return=1 and docstatus=1
|
where supplier in %s and is_return=1 and docstatus=1
|
||||||
and posting_date between %s and %s
|
and posting_date between %s and %s %s
|
||||||
""", (supplier, condition, year_start_date, year_end_date)))
|
""", (tuple(suppliers), year_start_date, year_end_date, condition)))
|
||||||
|
|
||||||
|
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
|
||||||
|
if current_amount < (certificate_limit - deducted_amount):
|
||||||
|
return current_amount * rate/100
|
||||||
|
else:
|
||||||
|
ltds_amount = (certificate_limit - deducted_amount)
|
||||||
|
tds_amount = current_amount - ltds_amount
|
||||||
|
|
||||||
|
return ltds_amount * rate/100 + tds_amount * tax_details.rate/100
|
||||||
|
|
||||||
|
def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit):
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
if ((getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and
|
||||||
|
certificate_limit > deducted_amount):
|
||||||
|
valid = True
|
||||||
|
|
||||||
|
return valid
|
@ -84,6 +84,7 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
|||||||
|
|
||||||
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
||||||
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
||||||
|
company_currency = get_company_currency(filters)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
data.extend(income or [])
|
data.extend(income or [])
|
||||||
@ -93,7 +94,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
|||||||
|
|
||||||
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
|
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
|
||||||
|
|
||||||
report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, True)
|
report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True)
|
||||||
|
|
||||||
return data, None, chart, report_summary
|
return data, None, chart, report_summary
|
||||||
|
|
||||||
|
@ -55,27 +55,27 @@ def get_columns(group_wise_columns, filters):
|
|||||||
columns = []
|
columns = []
|
||||||
column_map = frappe._dict({
|
column_map = frappe._dict({
|
||||||
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
|
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
|
||||||
"posting_date": _("Posting Date") + ":Date",
|
"posting_date": _("Posting Date") + ":Date:100",
|
||||||
"posting_time": _("Posting Time"),
|
"posting_time": _("Posting Time") + ":Data:100",
|
||||||
"item_code": _("Item Code") + ":Link/Item",
|
"item_code": _("Item Code") + ":Link/Item:100",
|
||||||
"item_name": _("Item Name"),
|
"item_name": _("Item Name") + ":Data:100",
|
||||||
"item_group": _("Item Group") + ":Link/Item Group",
|
"item_group": _("Item Group") + ":Link/Item Group:100",
|
||||||
"brand": _("Brand"),
|
"brand": _("Brand") + ":Link/Brand:100",
|
||||||
"description": _("Description"),
|
"description": _("Description") +":Data:100",
|
||||||
"warehouse": _("Warehouse") + ":Link/Warehouse",
|
"warehouse": _("Warehouse") + ":Link/Warehouse:100",
|
||||||
"qty": _("Qty") + ":Float",
|
"qty": _("Qty") + ":Float:80",
|
||||||
"base_rate": _("Avg. Selling Rate") + ":Currency/currency",
|
"base_rate": _("Avg. Selling Rate") + ":Currency/currency:100",
|
||||||
"buying_rate": _("Valuation Rate") + ":Currency/currency",
|
"buying_rate": _("Valuation Rate") + ":Currency/currency:100",
|
||||||
"base_amount": _("Selling Amount") + ":Currency/currency",
|
"base_amount": _("Selling Amount") + ":Currency/currency:100",
|
||||||
"buying_amount": _("Buying Amount") + ":Currency/currency",
|
"buying_amount": _("Buying Amount") + ":Currency/currency:100",
|
||||||
"gross_profit": _("Gross Profit") + ":Currency/currency",
|
"gross_profit": _("Gross Profit") + ":Currency/currency:100",
|
||||||
"gross_profit_percent": _("Gross Profit %") + ":Percent",
|
"gross_profit_percent": _("Gross Profit %") + ":Percent:100",
|
||||||
"project": _("Project") + ":Link/Project",
|
"project": _("Project") + ":Link/Project:100",
|
||||||
"sales_person": _("Sales person"),
|
"sales_person": _("Sales person"),
|
||||||
"allocated_amount": _("Allocated Amount") + ":Currency/currency",
|
"allocated_amount": _("Allocated Amount") + ":Currency/currency:100",
|
||||||
"customer": _("Customer") + ":Link/Customer",
|
"customer": _("Customer") + ":Link/Customer:100",
|
||||||
"customer_group": _("Customer Group") + ":Link/Customer Group",
|
"customer_group": _("Customer Group") + ":Link/Customer Group:100",
|
||||||
"territory": _("Territory") + ":Link/Territory"
|
"territory": _("Territory") + ":Link/Territory:100"
|
||||||
})
|
})
|
||||||
|
|
||||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||||
@ -85,7 +85,8 @@ def get_columns(group_wise_columns, filters):
|
|||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"label" : _("Currency"),
|
"label" : _("Currency"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Currency"
|
"options": "Currency",
|
||||||
|
"hidden": 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
@ -277,7 +278,7 @@ class GrossProfitGenerator(object):
|
|||||||
from `tabPurchase Invoice Item` a
|
from `tabPurchase Invoice Item` a
|
||||||
where a.item_code = %s and a.docstatus=1
|
where a.item_code = %s and a.docstatus=1
|
||||||
and modified <= %s
|
and modified <= %s
|
||||||
order by a.modified desc limit 1""", (item_code,self.filters.to_date))
|
order by a.modified desc limit 1""", (item_code, self.filters.to_date))
|
||||||
else:
|
else:
|
||||||
last_purchase_rate = frappe.db.sql("""
|
last_purchase_rate = frappe.db.sql("""
|
||||||
select (a.base_rate / a.conversion_factor)
|
select (a.base_rate / a.conversion_factor)
|
||||||
|
@ -44,9 +44,14 @@ def get_result(filters):
|
|||||||
out = []
|
out = []
|
||||||
for supplier in filters.supplier:
|
for supplier in filters.supplier:
|
||||||
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
||||||
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year][0]
|
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
|
||||||
|
|
||||||
|
if rate:
|
||||||
|
rate = rate[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
||||||
|
|
||||||
except IndexError:
|
except IndexError:
|
||||||
account = []
|
account = []
|
||||||
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
||||||
@ -76,7 +81,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
|
|||||||
supplier_credit_amount = flt(sum([d.credit for d in entries]))
|
supplier_credit_amount = flt(sum([d.credit for d in entries]))
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
vouchers = [d.voucher_no for d in entries]
|
||||||
vouchers += get_advance_vouchers(supplier, company=company,
|
vouchers += get_advance_vouchers([supplier], company=company,
|
||||||
from_date=from_date, to_date=to_date)
|
from_date=from_date, to_date=to_date)
|
||||||
|
|
||||||
tds_deducted = 0
|
tds_deducted = 0
|
||||||
@ -89,7 +94,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
|
|||||||
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
||||||
(account, from_date, to_date, company))[0][0])
|
(account, from_date, to_date, company))[0][0])
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount(supplier, from_date, to_date, company=company)
|
debit_note_amount = get_debit_note_amount([supplier], from_date, to_date, company=company)
|
||||||
|
|
||||||
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
||||||
|
|
||||||
|
@ -27,15 +27,6 @@ frappe.ui.form.on("Purchase Order", {
|
|||||||
frm.set_indicator_formatter('item_code',
|
frm.set_indicator_formatter('item_code',
|
||||||
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
|
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
|
||||||
|
|
||||||
frm.set_query("blanket_order", "items", function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"docstatus": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("expense_account", "items", function() {
|
frm.set_query("expense_account", "items", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_expense_account",
|
query: "erpnext.controllers.queries.get_expense_account",
|
||||||
@ -365,9 +356,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
|
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
|
||||||
source_doctype: "Material Request",
|
source_doctype: "Material Request",
|
||||||
target: me.frm,
|
target: me.frm,
|
||||||
setters: {
|
setters: {},
|
||||||
company: me.frm.doc.company
|
|
||||||
},
|
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
material_request_type: "Purchase",
|
material_request_type: "Purchase",
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
@ -384,7 +373,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
source_doctype: "Supplier Quotation",
|
source_doctype: "Supplier Quotation",
|
||||||
target: me.frm,
|
target: me.frm,
|
||||||
setters: {
|
setters: {
|
||||||
company: me.frm.doc.company
|
supplier: me.frm.doc.supplier
|
||||||
},
|
},
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
|
@ -54,11 +54,6 @@
|
|||||||
"items_section",
|
"items_section",
|
||||||
"scan_barcode",
|
"scan_barcode",
|
||||||
"items",
|
"items",
|
||||||
"section_break_48",
|
|
||||||
"pricing_rules",
|
|
||||||
"raw_material_details",
|
|
||||||
"set_reserve_warehouse",
|
|
||||||
"supplied_items",
|
|
||||||
"sb_last_purchase",
|
"sb_last_purchase",
|
||||||
"total_qty",
|
"total_qty",
|
||||||
"base_total",
|
"base_total",
|
||||||
@ -67,6 +62,11 @@
|
|||||||
"total_net_weight",
|
"total_net_weight",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
|
"section_break_48",
|
||||||
|
"pricing_rules",
|
||||||
|
"raw_material_details",
|
||||||
|
"set_reserve_warehouse",
|
||||||
|
"supplied_items",
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"tax_category",
|
"tax_category",
|
||||||
"column_break_50",
|
"column_break_50",
|
||||||
@ -105,23 +105,25 @@
|
|||||||
"payment_schedule_section",
|
"payment_schedule_section",
|
||||||
"payment_terms_template",
|
"payment_terms_template",
|
||||||
"payment_schedule",
|
"payment_schedule",
|
||||||
|
"tracking_section",
|
||||||
|
"per_billed",
|
||||||
|
"column_break_75",
|
||||||
|
"per_received",
|
||||||
"terms_section_break",
|
"terms_section_break",
|
||||||
"tc_name",
|
"tc_name",
|
||||||
"terms",
|
"terms",
|
||||||
"more_info",
|
"more_info",
|
||||||
"status",
|
"status",
|
||||||
"ref_sq",
|
"ref_sq",
|
||||||
|
"column_break_74",
|
||||||
"party_account_currency",
|
"party_account_currency",
|
||||||
"inter_company_order_reference",
|
"inter_company_order_reference",
|
||||||
"column_break_74",
|
|
||||||
"per_received",
|
|
||||||
"per_billed",
|
|
||||||
"column_break5",
|
"column_break5",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
"select_print_heading",
|
"select_print_heading",
|
||||||
"column_break_86",
|
"column_break_86",
|
||||||
"group_same_items",
|
|
||||||
"language",
|
"language",
|
||||||
|
"group_same_items",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
"from_date",
|
"from_date",
|
||||||
"to_date",
|
"to_date",
|
||||||
@ -220,7 +222,7 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "schedule_date",
|
"fieldname": "schedule_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Reqd By Date"
|
"label": "Required By"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
@ -432,6 +434,7 @@
|
|||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "Sets 'Warehouse' in each row of the Items table.",
|
||||||
"fieldname": "set_warehouse",
|
"fieldname": "set_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Set Target Warehouse",
|
"label": "Set Target Warehouse",
|
||||||
@ -827,6 +830,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"fieldname": "payment_schedule_section",
|
"fieldname": "payment_schedule_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Payment Terms"
|
"label": "Payment Terms"
|
||||||
@ -917,7 +921,8 @@
|
|||||||
"fieldname": "inter_company_order_reference",
|
"fieldname": "inter_company_order_reference",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Inter Company Order Reference",
|
"label": "Inter Company Order Reference",
|
||||||
"options": "Sales Order"
|
"options": "Sales Order",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_74",
|
"fieldname": "column_break_74",
|
||||||
@ -930,8 +935,6 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "% Received",
|
"label": "% Received",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "per_received",
|
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -942,8 +945,6 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "% Billed",
|
"label": "% Billed",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "per_billed",
|
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -998,6 +999,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"fieldname": "subscription_section",
|
"fieldname": "subscription_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Subscription Section"
|
"label": "Subscription Section"
|
||||||
@ -1050,13 +1052,23 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Set Reserve Warehouse",
|
"label": "Set Reserve Warehouse",
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "tracking_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Tracking"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_75",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-17 13:04:28.185197",
|
"modified": "2020-04-24 12:13:14.186280",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
@ -18,10 +18,6 @@
|
|||||||
"col_break1",
|
"col_break1",
|
||||||
"image",
|
"image",
|
||||||
"image_view",
|
"image_view",
|
||||||
"manufacture_details",
|
|
||||||
"manufacturer",
|
|
||||||
"column_break_14",
|
|
||||||
"manufacturer_part_no",
|
|
||||||
"quantity_and_rate",
|
"quantity_and_rate",
|
||||||
"qty",
|
"qty",
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
@ -44,7 +40,6 @@
|
|||||||
"base_amount",
|
"base_amount",
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"is_free_item",
|
"is_free_item",
|
||||||
"is_fixed_asset",
|
|
||||||
"section_break_29",
|
"section_break_29",
|
||||||
"net_rate",
|
"net_rate",
|
||||||
"net_amount",
|
"net_amount",
|
||||||
@ -52,11 +47,6 @@
|
|||||||
"base_net_rate",
|
"base_net_rate",
|
||||||
"base_net_amount",
|
"base_net_amount",
|
||||||
"billed_amt",
|
"billed_amt",
|
||||||
"item_weight_details",
|
|
||||||
"weight_per_unit",
|
|
||||||
"total_weight",
|
|
||||||
"column_break_40",
|
|
||||||
"weight_uom",
|
|
||||||
"warehouse_and_reference",
|
"warehouse_and_reference",
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"delivered_by_supplier",
|
"delivered_by_supplier",
|
||||||
@ -80,20 +70,31 @@
|
|||||||
"column_break_60",
|
"column_break_60",
|
||||||
"received_qty",
|
"received_qty",
|
||||||
"returned_qty",
|
"returned_qty",
|
||||||
|
"manufacture_details",
|
||||||
|
"manufacturer",
|
||||||
|
"column_break_14",
|
||||||
|
"manufacturer_part_no",
|
||||||
|
"more_info_section_break",
|
||||||
|
"is_fixed_asset",
|
||||||
|
"item_tax_rate",
|
||||||
"accounting_details",
|
"accounting_details",
|
||||||
"expense_account",
|
"expense_account",
|
||||||
"column_break_68",
|
"column_break_68",
|
||||||
|
"item_weight_details",
|
||||||
|
"weight_per_unit",
|
||||||
|
"total_weight",
|
||||||
|
"column_break_40",
|
||||||
|
"weight_uom",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"section_break_72",
|
"section_break_72",
|
||||||
"page_break",
|
"page_break"
|
||||||
"item_tax_rate"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"columns": 3,
|
"columns": 2,
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -133,7 +134,7 @@
|
|||||||
"fieldname": "schedule_date",
|
"fieldname": "schedule_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Reqd By Date",
|
"label": "Required By",
|
||||||
"oldfieldname": "schedule_date",
|
"oldfieldname": "schedule_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -216,15 +217,16 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 1,
|
||||||
"fieldname": "uom",
|
"fieldname": "uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
"oldfieldname": "uom",
|
"oldfieldname": "uom",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"reqd": 1,
|
"reqd": 1
|
||||||
"width": "100px"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "conversion_factor",
|
"fieldname": "conversion_factor",
|
||||||
@ -685,6 +687,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"fieldname": "manufacture_details",
|
"fieldname": "manufacture_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Manufacture"
|
"label": "Manufacture"
|
||||||
@ -717,12 +720,17 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Fixed Asset",
|
"label": "Is Fixed Asset",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "more_info_section_break",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "More Information"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-07 18:35:17.558928",
|
"modified": "2020-04-21 11:55:58.643393",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
@ -1,20 +1,26 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2013-02-22 01:27:42",
|
"creation": "2013-02-22 01:27:42",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"main_item_code",
|
"main_item_code",
|
||||||
"rm_item_code",
|
|
||||||
"required_qty",
|
|
||||||
"supplied_qty",
|
|
||||||
"rate",
|
|
||||||
"amount",
|
|
||||||
"column_break_6",
|
|
||||||
"bom_detail_no",
|
"bom_detail_no",
|
||||||
"reference_name",
|
|
||||||
"conversion_factor",
|
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
"reserve_warehouse"
|
"conversion_factor",
|
||||||
|
"column_break_6",
|
||||||
|
"rm_item_code",
|
||||||
|
"reference_name",
|
||||||
|
"reserve_warehouse",
|
||||||
|
"section_break2",
|
||||||
|
"rate",
|
||||||
|
"col_break2",
|
||||||
|
"amount",
|
||||||
|
"section_break1",
|
||||||
|
"required_qty",
|
||||||
|
"col_break1",
|
||||||
|
"supplied_qty"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -120,15 +126,34 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Supplied Qty",
|
"label": "Supplied Qty",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break1",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "col_break1",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break2",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "col_break2",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-08-20 13:37:32.702068",
|
"links": [],
|
||||||
|
"modified": "2020-03-12 15:43:53.862897",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item Supplied",
|
"name": "Purchase Order Item Supplied",
|
||||||
"owner": "dhanalekshmi@webnotestech.com",
|
"owner": "dhanalekshmi@webnotestech.com",
|
||||||
"permissions": []
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
}
|
}
|
@ -23,15 +23,11 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Payments'),
|
'label': _('Payments'),
|
||||||
'items': ['Payment Entry']
|
'items': ['Payment Entry', 'Bank Account']
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': _('Bank'),
|
|
||||||
'items': ['Bank Account']
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Pricing'),
|
'label': _('Pricing'),
|
||||||
'items': ['Pricing Rule']
|
'items': ['Pricing Rule']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -141,19 +141,18 @@ def get_conditions(filters):
|
|||||||
conditions = ""
|
conditions = ""
|
||||||
|
|
||||||
if filters.get("company"):
|
if filters.get("company"):
|
||||||
conditions += " AND company=%s"% frappe.db.escape(filters.get('company'))
|
conditions += " AND par.company=%s" % frappe.db.escape(filters.get('company'))
|
||||||
|
|
||||||
if filters.get("cost_center") or filters.get("project"):
|
if filters.get("cost_center") or filters.get("project"):
|
||||||
conditions += """
|
conditions += """
|
||||||
AND (cost_center=%s
|
AND (child.`cost_center`=%s OR child.`project`=%s)
|
||||||
OR project=%s)
|
""" % (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
|
||||||
"""% (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
|
|
||||||
|
|
||||||
if filters.get("from_date"):
|
if filters.get("from_date"):
|
||||||
conditions += " AND transaction_date>=%s"% filters.get('from_date')
|
conditions += " AND par.transaction_date>='%s'" % filters.get('from_date')
|
||||||
|
|
||||||
if filters.get("to_date"):
|
if filters.get("to_date"):
|
||||||
conditions += " AND transaction_date<=%s"% filters.get('to_date')
|
conditions += " AND par.transaction_date<='%s'" % filters.get('to_date')
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
@ -162,7 +161,6 @@ def get_data(filters):
|
|||||||
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
|
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
|
||||||
pr_records = get_mapped_pr_records()
|
pr_records = get_mapped_pr_records()
|
||||||
pi_records = get_mapped_pi_records()
|
pi_records = get_mapped_pi_records()
|
||||||
print(pi_records)
|
|
||||||
|
|
||||||
procurement_record=[]
|
procurement_record=[]
|
||||||
if procurement_record_against_mr:
|
if procurement_record_against_mr:
|
||||||
@ -198,16 +196,16 @@ def get_mapped_mr_details(conditions):
|
|||||||
mr_records = {}
|
mr_records = {}
|
||||||
mr_details = frappe.db.sql("""
|
mr_details = frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
mr.transaction_date,
|
par.transaction_date,
|
||||||
mr.per_ordered,
|
par.per_ordered,
|
||||||
mr_item.name,
|
child.name,
|
||||||
mr_item.parent,
|
child.parent,
|
||||||
mr_item.amount
|
child.amount
|
||||||
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
FROM `tabMaterial Request` par, `tabMaterial Request Item` child
|
||||||
WHERE
|
WHERE
|
||||||
mr.per_ordered>=0
|
par.per_ordered>=0
|
||||||
AND mr.name=mr_item.parent
|
AND par.name=child.parent
|
||||||
AND mr.docstatus=1
|
AND par.docstatus=1
|
||||||
{conditions}
|
{conditions}
|
||||||
""".format(conditions=conditions), as_dict=1) #nosec
|
""".format(conditions=conditions), as_dict=1) #nosec
|
||||||
|
|
||||||
@ -254,29 +252,29 @@ def get_mapped_pr_records():
|
|||||||
def get_po_entries(conditions):
|
def get_po_entries(conditions):
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
po_item.name,
|
child.name,
|
||||||
po_item.parent,
|
child.parent,
|
||||||
po_item.cost_center,
|
child.cost_center,
|
||||||
po_item.project,
|
child.project,
|
||||||
po_item.warehouse,
|
child.warehouse,
|
||||||
po_item.material_request,
|
child.material_request,
|
||||||
po_item.material_request_item,
|
child.material_request_item,
|
||||||
po_item.description,
|
child.description,
|
||||||
po_item.stock_uom,
|
child.stock_uom,
|
||||||
po_item.qty,
|
child.qty,
|
||||||
po_item.amount,
|
child.amount,
|
||||||
po_item.base_amount,
|
child.base_amount,
|
||||||
po_item.schedule_date,
|
child.schedule_date,
|
||||||
po.transaction_date,
|
par.transaction_date,
|
||||||
po.supplier,
|
par.supplier,
|
||||||
po.status,
|
par.status,
|
||||||
po.owner
|
par.owner
|
||||||
FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item
|
FROM `tabPurchase Order` par, `tabPurchase Order Item` child
|
||||||
WHERE
|
WHERE
|
||||||
po.docstatus = 1
|
par.docstatus = 1
|
||||||
AND po.name = po_item.parent
|
AND par.name = child.parent
|
||||||
AND po.status not in ("Closed","Completed","Cancelled")
|
AND par.status not in ("Closed","Completed","Cancelled")
|
||||||
{conditions}
|
{conditions}
|
||||||
GROUP BY
|
GROUP BY
|
||||||
po.name,po_item.item_code
|
par.name, child.item_code
|
||||||
""".format(conditions=conditions), as_dict=1) #nosec
|
""".format(conditions=conditions), as_dict=1) #nosec
|
@ -371,6 +371,19 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
fields = ["name", "parent_account"],
|
fields = ["name", "parent_account"],
|
||||||
limit_start=start, limit_page_length=page_len, as_list=True)
|
limit_start=start, limit_page_length=page_len, as_list=True)
|
||||||
|
|
||||||
|
def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
|
||||||
|
return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date
|
||||||
|
from `tabBlanket Order` bo, `tabBlanket Order Item` boi
|
||||||
|
where
|
||||||
|
boi.parent = bo.name
|
||||||
|
and boi.item_code = {item_code}
|
||||||
|
and bo.blanket_order_type = '{blanket_order_type}'
|
||||||
|
and bo.company = {company}
|
||||||
|
and bo.docstatus = 1"""
|
||||||
|
.format(item_code = frappe.db.escape(filters.get("item")),
|
||||||
|
blanket_order_type = filters.get("blanket_order_type"),
|
||||||
|
company = frappe.db.escape(filters.get("company"))
|
||||||
|
))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.utils import cint, flt, cstr
|
from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
@ -55,6 +55,13 @@ class StockController(AccountsController):
|
|||||||
frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
|
frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
|
||||||
.format(d.idx, serial_no_data.name, d.batch_no))
|
.format(d.idx, serial_no_data.name, d.batch_no))
|
||||||
|
|
||||||
|
if d.qty > 0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
|
||||||
|
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
|
||||||
|
|
||||||
|
if expiry_date and getdate(expiry_date) < getdate(self.posting_date):
|
||||||
|
frappe.throw(_("Row #{0}: The batch {1} has already expired.")
|
||||||
|
.format(d.idx, get_link_to_form("Batch", d.get("batch_no"))))
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
||||||
default_cost_center=None):
|
default_cost_center=None):
|
||||||
|
|
||||||
|
@ -12,13 +12,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Settings",
|
"label": "Maintenance",
|
||||||
"links": "[\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sends Mails to lead or contact based on a Campaign schedule\",\n \"label\": \"Email Campaign\",\n \"name\": \"Email Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Send mass SMS to your contacts\",\n \"label\": \"SMS Center\",\n \"name\": \"SMS Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Logs for maintaining sms delivery status\",\n \"label\": \"SMS Log\",\n \"name\": \"SMS Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup SMS gateway settings\",\n \"label\": \"SMS Settings\",\n \"name\": \"SMS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n }\n]"
|
"links": "[\n {\n \"description\": \"Plan for maintenance visits.\",\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Visit report for maintenance call.\",\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Maintenance",
|
"label": "Campaign",
|
||||||
"links": "[\n {\n \"description\": \"Plan for maintenance visits.\",\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Visit report for maintenance call.\",\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n }\n]"
|
"links": "[\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sends Mails to lead or contact based on a Campaign schedule\",\n \"label\": \"Email Campaign\",\n \"name\": \"Email Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create and Schedule social media posts\",\n \"label\": \"Social Media Post\",\n \"name\": \"Social Media Post\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Settings",
|
||||||
|
"links": "[\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Send mass SMS to your contacts\",\n \"label\": \"SMS Center\",\n \"name\": \"SMS Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Logs for maintaining sms delivery status\",\n \"label\": \"SMS Log\",\n \"name\": \"SMS Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup SMS gateway settings\",\n \"label\": \"SMS Settings\",\n \"name\": \"SMS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Twitter Settings\",\n \"name\": \"Twitter Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"LinkedIn Settings\",\n \"name\": \"LinkedIn Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"category": "Modules",
|
"category": "Modules",
|
||||||
@ -33,7 +38,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "CRM",
|
"label": "CRM",
|
||||||
"modified": "2020-04-01 11:28:51.219999",
|
"modified": "2020-04-27 22:32:26.682911",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "CRM",
|
"name": "CRM",
|
||||||
@ -42,7 +47,7 @@
|
|||||||
"pin_to_top": 0,
|
"pin_to_top": 0,
|
||||||
"shortcuts": [
|
"shortcuts": [
|
||||||
{
|
{
|
||||||
"format": "Open",
|
"format": "{} Open",
|
||||||
"label": "Lead",
|
"label": "Lead",
|
||||||
"link_to": "Lead",
|
"link_to": "Lead",
|
||||||
"stats_filter": "{\"status\":\"Open\"}",
|
"stats_filter": "{\"status\":\"Open\"}",
|
||||||
|
@ -16,23 +16,27 @@ frappe.ui.form.on('Twitter Settings', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
refresh: function(frm){
|
refresh: function(frm){
|
||||||
let msg,color;
|
let msg, color, flag=false;
|
||||||
if (frm.doc.session_status == "Active"){
|
if (frm.doc.session_status == "Active"){
|
||||||
msg = __("Session Active");
|
msg = __("Session Active");
|
||||||
color = 'green';
|
color = 'green';
|
||||||
|
flag = true;
|
||||||
}
|
}
|
||||||
else {
|
else if(frm.doc.consumer_key && frm.doc.consumer_secret) {
|
||||||
msg = __("Session Not Active. Save doc to login.");
|
msg = __("Session Not Active. Save doc to login.");
|
||||||
color = 'red';
|
color = 'red';
|
||||||
|
flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.dashboard.set_headline_alert(
|
if (flag){
|
||||||
`<div class="row">
|
frm.dashboard.set_headline_alert(
|
||||||
<div class="col-xs-12">
|
`<div class="row">
|
||||||
<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">${msg}</span></span>
|
<div class="col-xs-12">
|
||||||
</div>
|
<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">${msg}</span></span>
|
||||||
</div>`
|
</div>
|
||||||
);
|
</div>`
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
login: function(frm){
|
login: function(frm){
|
||||||
if (frm.doc.consumer_key && frm.doc.consumer_secret){
|
if (frm.doc.consumer_key && frm.doc.consumer_secret){
|
||||||
|
@ -112,6 +112,8 @@ frappe.ui.form.on("Fees", {
|
|||||||
args: {
|
args: {
|
||||||
"dt": frm.doc.doctype,
|
"dt": frm.doc.doctype,
|
||||||
"dn": frm.doc.name,
|
"dn": frm.doc.name,
|
||||||
|
"party_type": "Student",
|
||||||
|
"party": frm.doc.student,
|
||||||
"recipient_id": frm.doc.student_email
|
"recipient_id": frm.doc.student_email
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
@ -75,7 +75,8 @@ class Fees(AccountsController):
|
|||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
if self.send_payment_request and self.student_email:
|
if self.send_payment_request and self.student_email:
|
||||||
pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.student_email,
|
pr = make_payment_request(party_type="Student", party=self.student, dt="Fees",
|
||||||
|
dn=self.name, recipient_id=self.student_email,
|
||||||
submit_doc=True, use_dummy_message=True)
|
submit_doc=True, use_dummy_message=True)
|
||||||
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
|
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ def get_data():
|
|||||||
'heatmap': True,
|
'heatmap': True,
|
||||||
'heatmap_message': _('This is based on the attendance of this Student'),
|
'heatmap_message': _('This is based on the attendance of this Student'),
|
||||||
'fieldname': 'student',
|
'fieldname': 'student',
|
||||||
|
'non_standard_fieldnames': {
|
||||||
|
'Bank Account': 'party'
|
||||||
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Admission'),
|
'label': _('Admission'),
|
||||||
@ -29,7 +32,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Fee'),
|
'label': _('Fee'),
|
||||||
'items': ['Fees']
|
'items': ['Fees', 'Bank Account']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ def validate_customer_created(patient):
|
|||||||
|
|
||||||
def get_fee_validity(patient_appointments):
|
def get_fee_validity(patient_appointments):
|
||||||
if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
|
if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
|
||||||
return
|
return []
|
||||||
|
|
||||||
items_to_invoice = []
|
items_to_invoice = []
|
||||||
for appointment in patient_appointments:
|
for appointment in patient_appointments:
|
||||||
@ -110,7 +110,7 @@ def get_lab_tests_to_invoice(patient):
|
|||||||
filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
|
filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
|
||||||
)
|
)
|
||||||
for lab_test in lab_tests:
|
for lab_test in lab_tests:
|
||||||
item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable'])
|
item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.template, ['item', 'is_billable'])
|
||||||
if is_billable:
|
if is_billable:
|
||||||
lab_tests_to_invoice.append({
|
lab_tests_to_invoice.append({
|
||||||
'reference_type': 'Lab Test',
|
'reference_type': 'Lab Test',
|
||||||
|
@ -87,11 +87,12 @@
|
|||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.status==\"On Leave\"",
|
"depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
|
||||||
"fieldname": "leave_type",
|
"fieldname": "leave_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Leave Type",
|
"label": "Leave Type",
|
||||||
|
"mandatory_depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
|
||||||
"oldfieldname": "leave_type",
|
"oldfieldname": "leave_type",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Leave Type"
|
"options": "Leave Type"
|
||||||
@ -100,6 +101,7 @@
|
|||||||
"fieldname": "leave_application",
|
"fieldname": "leave_application",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Leave Application",
|
"label": "Leave Application",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Leave Application",
|
"options": "Leave Application",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -175,7 +177,8 @@
|
|||||||
"icon": "fa fa-ok",
|
"icon": "fa fa-ok",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2020-02-19 14:25:32.945842",
|
"links": [],
|
||||||
|
"modified": "2020-04-11 11:40:14.319496",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Attendance",
|
"name": "Attendance",
|
||||||
|
@ -7,33 +7,15 @@ import frappe
|
|||||||
from frappe.utils import getdate, nowdate
|
from frappe.utils import getdate, nowdate
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cstr, get_datetime, get_datetime_str
|
from frappe.utils import cstr, get_datetime, formatdate
|
||||||
from frappe.utils import update_progress_bar
|
|
||||||
|
|
||||||
class Attendance(Document):
|
class Attendance(Document):
|
||||||
def validate_duplicate_record(self):
|
def validate(self):
|
||||||
res = frappe.db.sql("""select name from `tabAttendance` where employee = %s and attendance_date = %s
|
from erpnext.controllers.status_updater import validate_status
|
||||||
and name != %s and docstatus != 2""",
|
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
|
||||||
(self.employee, getdate(self.attendance_date), self.name))
|
self.validate_attendance_date()
|
||||||
if res:
|
self.validate_duplicate_record()
|
||||||
frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee))
|
self.check_leave_record()
|
||||||
|
|
||||||
def check_leave_record(self):
|
|
||||||
leave_record = frappe.db.sql("""select leave_type, half_day, half_day_date from `tabLeave Application`
|
|
||||||
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
|
||||||
and docstatus = 1""", (self.employee, self.attendance_date), as_dict=True)
|
|
||||||
if leave_record:
|
|
||||||
for d in leave_record:
|
|
||||||
if d.half_day_date == getdate(self.attendance_date):
|
|
||||||
self.status = 'Half Day'
|
|
||||||
frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date))
|
|
||||||
else:
|
|
||||||
self.status = 'On Leave'
|
|
||||||
self.leave_type = d.leave_type
|
|
||||||
frappe.msgprint(_("Employee {0} is on Leave on {1}").format(self.employee, self.attendance_date))
|
|
||||||
|
|
||||||
if self.status == "On Leave" and not leave_record:
|
|
||||||
frappe.throw(_("No leave record found for employee {0} for {1}").format(self.employee, self.attendance_date))
|
|
||||||
|
|
||||||
def validate_attendance_date(self):
|
def validate_attendance_date(self):
|
||||||
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
|
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
|
||||||
@ -44,19 +26,52 @@ class Attendance(Document):
|
|||||||
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
|
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
|
||||||
frappe.throw(_("Attendance date can not be less than employee's joining date"))
|
frappe.throw(_("Attendance date can not be less than employee's joining date"))
|
||||||
|
|
||||||
|
def validate_duplicate_record(self):
|
||||||
|
res = frappe.db.sql("""
|
||||||
|
select name from `tabAttendance`
|
||||||
|
where employee = %s
|
||||||
|
and attendance_date = %s
|
||||||
|
and name != %s
|
||||||
|
and docstatus != 2
|
||||||
|
""", (self.employee, getdate(self.attendance_date), self.name))
|
||||||
|
if res:
|
||||||
|
frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee))
|
||||||
|
|
||||||
|
def check_leave_record(self):
|
||||||
|
leave_record = frappe.db.sql("""
|
||||||
|
select leave_type, half_day, half_day_date
|
||||||
|
from `tabLeave Application`
|
||||||
|
where employee = %s
|
||||||
|
and %s between from_date and to_date
|
||||||
|
and status = 'Approved'
|
||||||
|
and docstatus = 1
|
||||||
|
""", (self.employee, self.attendance_date), as_dict=True)
|
||||||
|
if leave_record:
|
||||||
|
for d in leave_record:
|
||||||
|
self.leave_type = d.leave_type
|
||||||
|
if d.half_day_date == getdate(self.attendance_date):
|
||||||
|
self.status = 'Half Day'
|
||||||
|
frappe.msgprint(_("Employee {0} on Half day on {1}")
|
||||||
|
.format(self.employee, formatdate(self.attendance_date)))
|
||||||
|
else:
|
||||||
|
self.status = 'On Leave'
|
||||||
|
frappe.msgprint(_("Employee {0} is on Leave on {1}")
|
||||||
|
.format(self.employee, formatdate(self.attendance_date)))
|
||||||
|
|
||||||
|
if self.status in ("On Leave", "Half Day"):
|
||||||
|
if not leave_record:
|
||||||
|
frappe.msgprint(_("No leave record found for employee {0} on {1}")
|
||||||
|
.format(self.employee, formatdate(self.attendance_date)), alert=1)
|
||||||
|
elif self.leave_type:
|
||||||
|
self.leave_type = None
|
||||||
|
self.leave_application = None
|
||||||
|
|
||||||
def validate_employee(self):
|
def validate_employee(self):
|
||||||
emp = frappe.db.sql("select name from `tabEmployee` where name = %s and status = 'Active'",
|
emp = frappe.db.sql("select name from `tabEmployee` where name = %s and status = 'Active'",
|
||||||
self.employee)
|
self.employee)
|
||||||
if not emp:
|
if not emp:
|
||||||
frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee))
|
frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee))
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
from erpnext.controllers.status_updater import validate_status
|
|
||||||
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
|
|
||||||
self.validate_attendance_date()
|
|
||||||
self.validate_duplicate_record()
|
|
||||||
self.check_leave_record()
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
events = []
|
events = []
|
||||||
@ -90,18 +105,20 @@ def add_attendance(events, start, end, conditions=None):
|
|||||||
if e not in events:
|
if e not in events:
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
|
||||||
def mark_attendance(employee, attendance_date, status, shift=None):
|
def mark_attendance(employee, attendance_date, status, shift=None, leave_type=None, ignore_validate=False):
|
||||||
employee_doc = frappe.get_doc('Employee', employee)
|
|
||||||
if not frappe.db.exists('Attendance', {'employee':employee, 'attendance_date':attendance_date, 'docstatus':('!=', '2')}):
|
if not frappe.db.exists('Attendance', {'employee':employee, 'attendance_date':attendance_date, 'docstatus':('!=', '2')}):
|
||||||
doc_dict = {
|
company = frappe.db.get_value('Employee', employee, 'company')
|
||||||
|
attendance = frappe.get_doc({
|
||||||
'doctype': 'Attendance',
|
'doctype': 'Attendance',
|
||||||
'employee': employee,
|
'employee': employee,
|
||||||
'attendance_date': attendance_date,
|
'attendance_date': attendance_date,
|
||||||
'status': status,
|
'status': status,
|
||||||
'company': employee_doc.company,
|
'company': company,
|
||||||
'shift': shift
|
'shift': shift,
|
||||||
}
|
'leave_type': leave_type
|
||||||
attendance = frappe.get_doc(doc_dict).insert()
|
})
|
||||||
|
attendance.flags.ignore_validate = ignore_validate
|
||||||
|
attendance.insert()
|
||||||
attendance.submit()
|
attendance.submit()
|
||||||
return attendance.name
|
return attendance.name
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ def get_data():
|
|||||||
'heatmap': True,
|
'heatmap': True,
|
||||||
'heatmap_message': _('This is based on the attendance of this Employee'),
|
'heatmap_message': _('This is based on the attendance of this Employee'),
|
||||||
'fieldname': 'employee',
|
'fieldname': 'employee',
|
||||||
|
'non_standard_fieldnames': {
|
||||||
|
'Bank Account': 'party'
|
||||||
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Leave and Attendance'),
|
'label': _('Leave and Attendance'),
|
||||||
@ -33,7 +36,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Payroll'),
|
'label': _('Payroll'),
|
||||||
'items': ['Salary Structure Assignment', 'Salary Slip', 'Additional Salary', 'Timesheet','Employee Incentive', 'Retention Bonus']
|
'items': ['Salary Structure Assignment', 'Salary Slip', 'Additional Salary', 'Timesheet','Employee Incentive', 'Retention Bonus', 'Bank Account']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Training'),
|
'label': _('Training'),
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Employee Other Income', {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "HR-INCOME-.######",
|
||||||
|
"creation": "2020-03-18 15:04:40.767434",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"employee",
|
||||||
|
"employee_name",
|
||||||
|
"payroll_period",
|
||||||
|
"column_break_3",
|
||||||
|
"company",
|
||||||
|
"source",
|
||||||
|
"amount",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "employee",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Employee",
|
||||||
|
"options": "Employee",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "payroll_period",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Payroll Period",
|
||||||
|
"options": "Payroll Period",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "source",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Amount",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.employee_name",
|
||||||
|
"fieldname": "employee_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Employee Name",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amended_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Amended From",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Employee Other Income",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_submittable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-03-19 18:06:45.361830",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "HR",
|
||||||
|
"name": "Employee Other Income",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "HR Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "HR User",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Employee",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, 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 EmployeeOtherIncome(Document):
|
||||||
|
pass
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestEmployeeOtherIncome(unittest.TestCase):
|
||||||
|
pass
|
@ -1,620 +1,180 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_import": 1,
|
||||||
"allow_guest_to_view": 0,
|
"allow_rename": 1,
|
||||||
"allow_import": 1,
|
"autoname": "HR-TAX-DEC-.YYYY.-.#####",
|
||||||
"allow_rename": 1,
|
"creation": "2018-04-13 16:53:36.175504",
|
||||||
"autoname": "HR-TAX-DEC-.YYYY.-.#####",
|
"doctype": "DocType",
|
||||||
"beta": 0,
|
"editable_grid": 1,
|
||||||
"creation": "2018-04-13 16:53:36.175504",
|
"engine": "InnoDB",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"docstatus": 0,
|
"employee",
|
||||||
"doctype": "DocType",
|
"employee_name",
|
||||||
"document_type": "",
|
"department",
|
||||||
"editable_grid": 1,
|
"column_break_2",
|
||||||
"engine": "InnoDB",
|
"payroll_period",
|
||||||
|
"company",
|
||||||
|
"amended_from",
|
||||||
|
"section_break_8",
|
||||||
|
"declarations",
|
||||||
|
"section_break_10",
|
||||||
|
"total_declared_amount",
|
||||||
|
"column_break_12",
|
||||||
|
"total_exemption_amount"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "employee",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Employee",
|
||||||
"collapsible": 0,
|
"options": "Employee",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fetch_if_empty": 0,
|
},
|
||||||
"fieldname": "employee",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Employee",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Employee",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fetch_from": "employee.employee_name",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "employee_name",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Data",
|
||||||
"bold": 0,
|
"label": "Employee Name",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fetch_from": "employee.employee_name",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "employee_name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Employee Name",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fetch_from": "employee.department",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "department",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"label": "Department",
|
||||||
"collapsible": 0,
|
"options": "Department",
|
||||||
"columns": 0,
|
"read_only": 1
|
||||||
"fetch_from": "employee.department",
|
},
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "department",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Department",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Department",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_2",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "payroll_period",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Payroll Period",
|
||||||
"collapsible": 0,
|
"options": "Payroll Period",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fetch_if_empty": 0,
|
},
|
||||||
"fieldname": "payroll_period",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payroll Period",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payroll Period",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fetch_from": "employee.company",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "company",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"label": "Company",
|
||||||
"collapsible": 0,
|
"options": "Company"
|
||||||
"columns": 0,
|
},
|
||||||
"fetch_from": "employee.company",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "company",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "amended_from",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Amended From",
|
||||||
"bold": 0,
|
"no_copy": 1,
|
||||||
"collapsible": 0,
|
"options": "Employee Tax Exemption Declaration",
|
||||||
"columns": 0,
|
"print_hide": 1,
|
||||||
"fetch_if_empty": 0,
|
"read_only": 1
|
||||||
"fieldname": "amended_from",
|
},
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amended From",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Employee Tax Exemption Declaration",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_8",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_8",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "declarations",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Declarations",
|
||||||
"bold": 0,
|
"options": "Employee Tax Exemption Declaration Category"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "declarations",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Declarations",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Employee Tax Exemption Declaration Category",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_10",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_10",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "total_declared_amount",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Currency",
|
||||||
"allow_on_submit": 0,
|
"label": "Total Declared Amount",
|
||||||
"bold": 0,
|
"read_only": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "total_declared_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Declared Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_12",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_12",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "total_exemption_amount",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Currency",
|
||||||
"allow_on_submit": 0,
|
"label": "Total Exemption Amount",
|
||||||
"bold": 0,
|
"read_only": 1
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "total_exemption_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Exemption Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "other_incomes_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Other Incomes",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "income_from_other_sources",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Income From Other Sources",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"is_submittable": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2020-03-18 14:56:25.625717",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "HR",
|
||||||
"in_create": 0,
|
"name": "Employee Tax Exemption Declaration",
|
||||||
"is_submittable": 1,
|
"owner": "Administrator",
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-05-11 16:13:50.472670",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "HR",
|
|
||||||
"name": "Employee Tax Exemption Declaration",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "System Manager",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "HR Manager",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "HR Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "HR User",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "HR User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "Employee",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "Employee",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -8,31 +8,17 @@ from frappe.model.document import Document
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_annual_eligible_hra_exemption
|
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
|
||||||
|
calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
|
||||||
class DuplicateDeclarationError(frappe.ValidationError): pass
|
|
||||||
|
|
||||||
class EmployeeTaxExemptionDeclaration(Document):
|
class EmployeeTaxExemptionDeclaration(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
validate_tax_declaration(self.declarations)
|
validate_tax_declaration(self.declarations)
|
||||||
self.validate_duplicate()
|
validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
|
||||||
self.set_total_declared_amount()
|
self.set_total_declared_amount()
|
||||||
self.set_total_exemption_amount()
|
self.set_total_exemption_amount()
|
||||||
self.calculate_hra_exemption()
|
self.calculate_hra_exemption()
|
||||||
|
|
||||||
def validate_duplicate(self):
|
|
||||||
duplicate = frappe.db.get_value("Employee Tax Exemption Declaration",
|
|
||||||
filters = {
|
|
||||||
"employee": self.employee,
|
|
||||||
"payroll_period": self.payroll_period,
|
|
||||||
"name": ["!=", self.name],
|
|
||||||
"docstatus": ["!=", 2]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if duplicate:
|
|
||||||
frappe.throw(_("Duplicate Tax Declaration of {0} for period {1}")
|
|
||||||
.format(self.employee, self.payroll_period), DuplicateDeclarationError)
|
|
||||||
|
|
||||||
def set_total_declared_amount(self):
|
def set_total_declared_amount(self):
|
||||||
self.total_declared_amount = 0.0
|
self.total_declared_amount = 0.0
|
||||||
for d in self.declarations:
|
for d in self.declarations:
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import unittest
|
import unittest
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration import DuplicateDeclarationError
|
from erpnext.hr.utils import DuplicateDeclarationError
|
||||||
|
|
||||||
class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
"total_actual_amount",
|
"total_actual_amount",
|
||||||
"column_break_12",
|
"column_break_12",
|
||||||
"exemption_amount",
|
"exemption_amount",
|
||||||
"other_incomes_section",
|
|
||||||
"income_from_other_sources",
|
|
||||||
"attachment_section",
|
"attachment_section",
|
||||||
"attachments",
|
"attachments",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
@ -111,16 +109,6 @@
|
|||||||
"label": "Total Exemption Amount",
|
"label": "Total Exemption Amount",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "other_incomes_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Other Incomes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "income_from_other_sources",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"label": "Income From Other Sources"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "attachment_section",
|
"fieldname": "attachment_section",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
@ -142,7 +130,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-02 19:02:15.398486",
|
"modified": "2020-03-18 14:55:51.420016",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Tax Exemption Proof Submission",
|
"name": "Employee Tax Exemption Proof Submission",
|
||||||
|
@ -7,7 +7,8 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_hra_exemption_for_period
|
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
|
||||||
|
calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period
|
||||||
|
|
||||||
class EmployeeTaxExemptionProofSubmission(Document):
|
class EmployeeTaxExemptionProofSubmission(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@ -15,6 +16,7 @@ class EmployeeTaxExemptionProofSubmission(Document):
|
|||||||
self.set_total_actual_amount()
|
self.set_total_actual_amount()
|
||||||
self.set_total_exemption_amount()
|
self.set_total_exemption_amount()
|
||||||
self.calculate_hra_exemption()
|
self.calculate_hra_exemption()
|
||||||
|
validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
|
||||||
|
|
||||||
def set_total_actual_amount(self):
|
def set_total_actual_amount(self):
|
||||||
self.total_actual_amount = flt(self.get("house_rent_payment_amount"))
|
self.total_actual_amount = flt(self.get("house_rent_payment_amount"))
|
||||||
@ -32,4 +34,4 @@ class EmployeeTaxExemptionProofSubmission(Document):
|
|||||||
self.exemption_amount += hra_exemption["total_eligible_hra_exemption"]
|
self.exemption_amount += hra_exemption["total_eligible_hra_exemption"]
|
||||||
self.monthly_hra_exemption = hra_exemption["monthly_exemption"]
|
self.monthly_hra_exemption = hra_exemption["monthly_exemption"]
|
||||||
self.monthly_house_rent = hra_exemption["monthly_house_rent"]
|
self.monthly_house_rent = hra_exemption["monthly_house_rent"]
|
||||||
self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"]
|
self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"]
|
||||||
|
@ -17,7 +17,7 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account",
|
method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center",
|
||||||
args: {
|
args: {
|
||||||
"expense_claim_type": d.expense_type,
|
"expense_claim_type": d.expense_type,
|
||||||
"company": doc.company
|
"company": doc.company
|
||||||
@ -25,6 +25,7 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
|||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
d.default_account = r.message.account;
|
d.default_account = r.message.account;
|
||||||
|
d.cost_center = r.message.cost_center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe, erpnext
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import get_fullname, flt, cstr
|
from frappe.utils import get_fullname, flt, cstr, get_link_to_form
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import set_employee_name
|
from erpnext.hr.utils import set_employee_name
|
||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
@ -192,7 +192,8 @@ class ExpenseClaim(AccountsController):
|
|||||||
def validate_account_details(self):
|
def validate_account_details(self):
|
||||||
for data in self.expenses:
|
for data in self.expenses:
|
||||||
if not data.cost_center:
|
if not data.cost_center:
|
||||||
frappe.throw(_("Cost center is required to book an expense claim"))
|
frappe.throw(_("Row {0}: {1} is required in the expenses table to book an expense claim.")
|
||||||
|
.format(data.idx, frappe.bold("Cost Center")))
|
||||||
|
|
||||||
if self.is_paid:
|
if self.is_paid:
|
||||||
if not self.mode_of_payment:
|
if not self.mode_of_payment:
|
||||||
@ -308,13 +309,23 @@ def make_bank_entry(dt, dn):
|
|||||||
|
|
||||||
return je.as_dict()
|
return je.as_dict()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_expense_claim_account_and_cost_center(expense_claim_type, company):
|
||||||
|
data = get_expense_claim_account(expense_claim_type, company)
|
||||||
|
cost_center = erpnext.get_default_cost_center(company)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"account": data.get("account"),
|
||||||
|
"cost_center": cost_center
|
||||||
|
}
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_expense_claim_account(expense_claim_type, company):
|
def get_expense_claim_account(expense_claim_type, company):
|
||||||
account = frappe.db.get_value("Expense Claim Account",
|
account = frappe.db.get_value("Expense Claim Account",
|
||||||
{"parent": expense_claim_type, "company": company}, "default_account")
|
{"parent": expense_claim_type, "company": company}, "default_account")
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Please set default account in Expense Claim Type {0}")
|
frappe.throw(_("Set the default account for the {0} {1}")
|
||||||
.format(expense_claim_type))
|
.format(frappe.bold("Expense Claim Type"), get_link_to_form("Expense Claim Type", expense_claim_type)))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"account": account
|
"account": account
|
||||||
|
@ -13,10 +13,12 @@
|
|||||||
"stop_birthday_reminders",
|
"stop_birthday_reminders",
|
||||||
"expense_approver_mandatory_in_expense_claim",
|
"expense_approver_mandatory_in_expense_claim",
|
||||||
"payroll_settings",
|
"payroll_settings",
|
||||||
|
"payroll_based_on",
|
||||||
|
"max_working_hours_against_timesheet",
|
||||||
"include_holidays_in_total_working_days",
|
"include_holidays_in_total_working_days",
|
||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
"max_working_hours_against_timesheet",
|
|
||||||
"column_break_11",
|
"column_break_11",
|
||||||
|
"daily_wages_fraction_for_half_day",
|
||||||
"email_salary_slip_to_employee",
|
"email_salary_slip_to_employee",
|
||||||
"encrypt_salary_slips_in_emails",
|
"encrypt_salary_slips_in_emails",
|
||||||
"password_policy",
|
"password_policy",
|
||||||
@ -184,13 +186,27 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Role Allowed to Create Backdated Leave Application",
|
"label": "Role Allowed to Create Backdated Leave Application",
|
||||||
"options": "Role"
|
"options": "Role"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Leave",
|
||||||
|
"fieldname": "payroll_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Calculate Working Days in Payroll based on",
|
||||||
|
"options": "Leave\nAttendance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0.5",
|
||||||
|
"description": "The fraction of daily wages to be paid for half-day attendance",
|
||||||
|
"fieldname": "daily_wages_fraction_for_half_day",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Daily Wages Fraction for Half Day"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-01-06 18:46:30.189815",
|
"modified": "2020-04-13 21:20:59.382394",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR Settings",
|
"name": "HR Settings",
|
||||||
|
@ -15,6 +15,9 @@ class HRSettings(Document):
|
|||||||
self.set_naming_series()
|
self.set_naming_series()
|
||||||
self.validate_password_policy()
|
self.validate_password_policy()
|
||||||
|
|
||||||
|
if not self.daily_wages_fraction_for_half_day:
|
||||||
|
self.daily_wages_fraction_for_half_day = 0.5
|
||||||
|
|
||||||
def set_naming_series(self):
|
def set_naming_series(self):
|
||||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||||
set_by_naming_series("Employee", "employee_number",
|
set_by_naming_series("Employee", "employee_number",
|
||||||
|
0
erpnext/hr/doctype/income_tax_slab/__init__.py
Normal file
0
erpnext/hr/doctype/income_tax_slab/__init__.py
Normal file
6
erpnext/hr/doctype/income_tax_slab/income_tax_slab.js
Normal file
6
erpnext/hr/doctype/income_tax_slab/income_tax_slab.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Income Tax Slab', {
|
||||||
|
|
||||||
|
});
|
160
erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
Normal file
160
erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "Prompt",
|
||||||
|
"creation": "2020-03-17 16:50:35.564915",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"effective_from",
|
||||||
|
"company",
|
||||||
|
"column_break_3",
|
||||||
|
"allow_tax_exemption",
|
||||||
|
"standard_tax_exemption_amount",
|
||||||
|
"disabled",
|
||||||
|
"amended_from",
|
||||||
|
"taxable_salary_slabs_section",
|
||||||
|
"slabs",
|
||||||
|
"taxes_and_charges_on_income_tax_section",
|
||||||
|
"other_taxes_and_charges"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "effective_from",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Effective from",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "If enabled, Tax Exemption Declaration will be considered for income tax calculation.",
|
||||||
|
"fieldname": "allow_tax_exemption",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Allow Tax Exemption"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "taxable_salary_slabs_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Taxable Salary Slabs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amended_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Amended From",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Income Tax Slab",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "slabs",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Taxable Salary Slabs",
|
||||||
|
"options": "Taxable Salary Slab",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "allow_tax_exemption",
|
||||||
|
"fieldname": "standard_tax_exemption_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Standard Tax Exemption Amount",
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "taxes_and_charges_on_income_tax_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Taxes and Charges on Income Tax"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "other_taxes_and_charges",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Other Taxes and Charges",
|
||||||
|
"options": "Income Tax Slab Other Charges"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_submittable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-04-24 12:28:36.805904",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "HR",
|
||||||
|
"name": "Income Tax Slab",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cancel": 1,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "HR Manager",
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cancel": 1,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "HR User",
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cancel": 1,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Administrator",
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
10
erpnext/hr/doctype/income_tax_slab/income_tax_slab.py
Normal file
10
erpnext/hr/doctype/income_tax_slab/income_tax_slab.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, 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 IncomeTaxSlab(Document):
|
||||||
|
pass
|
10
erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py
Normal file
10
erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestIncomeTaxSlab(unittest.TestCase):
|
||||||
|
pass
|
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2020-04-24 11:46:59.041180",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"description",
|
||||||
|
"column_break_2",
|
||||||
|
"percent",
|
||||||
|
"conditions_section",
|
||||||
|
"min_taxable_income",
|
||||||
|
"column_break_7",
|
||||||
|
"max_taxable_income"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_2",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "min_taxable_income",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Min Taxable Income",
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 4,
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Description",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "percent",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Percent",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "conditions_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Conditions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_7",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "max_taxable_income",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Max Taxable Income",
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-04-24 13:27:43.598967",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "HR",
|
||||||
|
"name": "Income Tax Slab Other Charges",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, 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 IncomeTaxSlabOtherCharges(Document):
|
||||||
|
pass
|
@ -30,16 +30,16 @@ class LeaveAllocation(Document):
|
|||||||
def validate_leave_allocation_days(self):
|
def validate_leave_allocation_days(self):
|
||||||
company = frappe.db.get_value("Employee", self.employee, "company")
|
company = frappe.db.get_value("Employee", self.employee, "company")
|
||||||
leave_period = get_leave_period(self.from_date, self.to_date, company)
|
leave_period = get_leave_period(self.from_date, self.to_date, company)
|
||||||
max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
|
max_leaves_allowed = flt(frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed"))
|
||||||
if max_leaves_allowed > 0:
|
if max_leaves_allowed > 0:
|
||||||
leave_allocated = 0
|
leave_allocated = 0
|
||||||
if leave_period:
|
if leave_period:
|
||||||
leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type,
|
leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type,
|
||||||
leave_period[0].from_date, leave_period[0].to_date)
|
leave_period[0].from_date, leave_period[0].to_date)
|
||||||
leave_allocated += self.new_leaves_allocated
|
leave_allocated += flt(self.new_leaves_allocated)
|
||||||
if leave_allocated > max_leaves_allowed:
|
if leave_allocated > max_leaves_allowed:
|
||||||
frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")
|
frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")
|
||||||
.format(self.leave_type, self.employee))
|
.format(self.leave_type, self.employee))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.create_leave_ledger_entry()
|
self.create_leave_ledger_entry()
|
||||||
|
@ -374,7 +374,8 @@ class LeaveApplication(Document):
|
|||||||
leaves=self.total_leave_days * -1,
|
leaves=self.total_leave_days * -1,
|
||||||
from_date=self.from_date,
|
from_date=self.from_date,
|
||||||
to_date=self.to_date,
|
to_date=self.to_date,
|
||||||
is_lwp=lwp
|
is_lwp=lwp,
|
||||||
|
holiday_list=get_holiday_list_for_employee(self.employee)
|
||||||
)
|
)
|
||||||
create_leave_ledger_entry(self, args, submit)
|
create_leave_ledger_entry(self, args, submit)
|
||||||
|
|
||||||
@ -384,7 +385,9 @@ class LeaveApplication(Document):
|
|||||||
from_date=self.from_date,
|
from_date=self.from_date,
|
||||||
to_date=expiry_date,
|
to_date=expiry_date,
|
||||||
leaves=(date_diff(expiry_date, self.from_date) + 1) * -1,
|
leaves=(date_diff(expiry_date, self.from_date) + 1) * -1,
|
||||||
is_lwp=lwp
|
is_lwp=lwp,
|
||||||
|
holiday_list=get_holiday_list_for_employee(self.employee),
|
||||||
|
|
||||||
)
|
)
|
||||||
create_leave_ledger_entry(self, args, submit)
|
create_leave_ledger_entry(self, args, submit)
|
||||||
|
|
||||||
@ -410,7 +413,7 @@ def get_allocation_expiry(employee, leave_type, to_date, from_date):
|
|||||||
return expiry[0]['to_date'] if expiry else None
|
return expiry[0]['to_date'] if expiry else None
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None):
|
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None, holiday_list = None):
|
||||||
number_of_days = 0
|
number_of_days = 0
|
||||||
if cint(half_day) == 1:
|
if cint(half_day) == 1:
|
||||||
if from_date == to_date:
|
if from_date == to_date:
|
||||||
@ -424,7 +427,7 @@ def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day
|
|||||||
number_of_days = date_diff(to_date, from_date) + 1
|
number_of_days = date_diff(to_date, from_date) + 1
|
||||||
|
|
||||||
if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"):
|
if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"):
|
||||||
number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date))
|
number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date, holiday_list=holiday_list))
|
||||||
return number_of_days
|
return number_of_days
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -575,7 +578,7 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date):
|
|||||||
{'name': leave_entry.transaction_name}, ['half_day_date'])
|
{'name': leave_entry.transaction_name}, ['half_day_date'])
|
||||||
|
|
||||||
leave_days += get_number_of_leave_days(employee, leave_type,
|
leave_days += get_number_of_leave_days(employee, leave_type,
|
||||||
leave_entry.from_date, leave_entry.to_date, half_day, half_day_date) * -1
|
leave_entry.from_date, leave_entry.to_date, half_day, half_day_date, holiday_list=leave_entry.holiday_list) * -1
|
||||||
|
|
||||||
return leave_days
|
return leave_days
|
||||||
|
|
||||||
@ -589,7 +592,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date):
|
|||||||
''' Returns leave entries between from_date and to_date. '''
|
''' Returns leave entries between from_date and to_date. '''
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type,
|
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list,
|
||||||
is_carry_forward, is_expired
|
is_carry_forward, is_expired
|
||||||
FROM `tabLeave Ledger Entry`
|
FROM `tabLeave Ledger Entry`
|
||||||
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
||||||
@ -607,9 +610,10 @@ def get_leave_entries(employee, leave_type, from_date, to_date):
|
|||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_holidays(employee, from_date, to_date):
|
def get_holidays(employee, from_date, to_date, holiday_list = None):
|
||||||
'''get holidays between two dates for the given employee'''
|
'''get holidays between two dates for the given employee'''
|
||||||
holiday_list = get_holiday_list_for_employee(employee)
|
if not holiday_list:
|
||||||
|
holiday_list = get_holiday_list_for_employee(employee)
|
||||||
|
|
||||||
holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2
|
holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2
|
||||||
where h1.parent = h2.name and h1.holiday_date between %s and %s
|
where h1.parent = h2.name and h1.holiday_date between %s and %s
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2019-05-09 15:47:39.760406",
|
"creation": "2019-05-09 15:47:39.760406",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
@ -12,6 +13,7 @@
|
|||||||
"column_break_7",
|
"column_break_7",
|
||||||
"from_date",
|
"from_date",
|
||||||
"to_date",
|
"to_date",
|
||||||
|
"holiday_list",
|
||||||
"is_carry_forward",
|
"is_carry_forward",
|
||||||
"is_expired",
|
"is_expired",
|
||||||
"is_lwp",
|
"is_lwp",
|
||||||
@ -98,11 +100,18 @@
|
|||||||
"fieldname": "is_lwp",
|
"fieldname": "is_lwp",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Leave Without Pay"
|
"label": "Is Leave Without Pay"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "holiday_list",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Holiday List",
|
||||||
|
"options": "Holiday List"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-08-20 14:40:04.130799",
|
"links": [],
|
||||||
|
"modified": "2020-02-27 14:40:10.502605",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Ledger Entry",
|
"name": "Leave Ledger Entry",
|
||||||
|
@ -1,401 +1,102 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_import": 1,
|
||||||
"allow_guest_to_view": 0,
|
"autoname": "Prompt",
|
||||||
"allow_import": 1,
|
"creation": "2018-04-13 15:18:53.698553",
|
||||||
"allow_rename": 0,
|
"doctype": "DocType",
|
||||||
"autoname": "Prompt",
|
"editable_grid": 1,
|
||||||
"beta": 0,
|
"engine": "InnoDB",
|
||||||
"creation": "2018-04-13 15:18:53.698553",
|
"field_order": [
|
||||||
"custom": 0,
|
"company",
|
||||||
"docstatus": 0,
|
"column_break_2",
|
||||||
"doctype": "DocType",
|
"start_date",
|
||||||
"document_type": "",
|
"end_date",
|
||||||
"editable_grid": 1,
|
"section_break_5",
|
||||||
"engine": "InnoDB",
|
"periods"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "company",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Company",
|
||||||
"collapsible": 0,
|
"options": "Company",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fetch_if_empty": 0,
|
},
|
||||||
"fieldname": "company",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_2",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "start_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"label": "Start Date",
|
||||||
"bold": 0,
|
"reqd": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "start_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Start Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "end_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"label": "End Date",
|
||||||
"bold": 0,
|
"reqd": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "end_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "End Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_5",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break",
|
||||||
"allow_on_submit": 0,
|
"hidden": 1,
|
||||||
"bold": 0,
|
"label": "Payroll Periods"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_5",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payroll Periods",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "periods",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Payroll Periods",
|
||||||
"bold": 0,
|
"options": "Payroll Period Date"
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "periods",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payroll Periods",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payroll Period Date",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_7",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Taxable Salary Slabs",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "taxable_salary_slabs",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Taxable Salary Slabs",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Taxable Salary Slab",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "standard_tax_exemption_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Standard Tax Exemption Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2020-03-18 18:13:23.859980",
|
||||||
"hide_toolbar": 0,
|
"modified_by": "Administrator",
|
||||||
"idx": 0,
|
"module": "HR",
|
||||||
"image_view": 0,
|
"name": "Payroll Period",
|
||||||
"in_create": 0,
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-04-26 01:45:03.160929",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "HR",
|
|
||||||
"name": "Payroll Period",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"email": 1,
|
||||||
"delete": 1,
|
"export": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 0,
|
"role": "System Manager",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"email": 1,
|
||||||
"delete": 1,
|
"export": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 0,
|
"role": "HR Manager",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "HR Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"email": 1,
|
||||||
"delete": 1,
|
"export": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 0,
|
"role": "HR User",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "HR User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -45,8 +45,9 @@ class PayrollPeriod(Document):
|
|||||||
+ _(") for {0}").format(self.company)
|
+ _(") for {0}").format(self.company)
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
def get_payroll_period_days(start_date, end_date, employee):
|
def get_payroll_period_days(start_date, end_date, employee, company=None):
|
||||||
company = frappe.db.get_value("Employee", employee, "company")
|
if not company:
|
||||||
|
company = frappe.db.get_value("Employee", employee, "company")
|
||||||
payroll_period = frappe.db.sql("""
|
payroll_period = frappe.db.sql("""
|
||||||
select name, start_date, end_date
|
select name, start_date, end_date
|
||||||
from `tabPayroll Period`
|
from `tabPayroll Period`
|
||||||
|
@ -1,264 +1,263 @@
|
|||||||
{
|
{
|
||||||
"allow_import": 1,
|
"actions": [],
|
||||||
"allow_rename": 1,
|
"allow_import": 1,
|
||||||
"autoname": "field:salary_component",
|
"allow_rename": 1,
|
||||||
"creation": "2016-06-30 15:42:43.631931",
|
"autoname": "field:salary_component",
|
||||||
"doctype": "DocType",
|
"creation": "2016-06-30 15:42:43.631931",
|
||||||
"document_type": "Setup",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"document_type": "Setup",
|
||||||
"engine": "InnoDB",
|
"editable_grid": 1,
|
||||||
"field_order": [
|
"engine": "InnoDB",
|
||||||
"salary_component",
|
"field_order": [
|
||||||
"salary_component_abbr",
|
"salary_component",
|
||||||
"type",
|
"salary_component_abbr",
|
||||||
"description",
|
"type",
|
||||||
"column_break_4",
|
"description",
|
||||||
"is_payable",
|
"column_break_4",
|
||||||
"depends_on_payment_days",
|
"depends_on_payment_days",
|
||||||
"is_tax_applicable",
|
"is_tax_applicable",
|
||||||
"deduct_full_tax_on_selected_payroll_date",
|
"deduct_full_tax_on_selected_payroll_date",
|
||||||
"round_to_the_nearest_integer",
|
"variable_based_on_taxable_salary",
|
||||||
"statistical_component",
|
"exempted_from_income_tax",
|
||||||
"do_not_include_in_total",
|
"round_to_the_nearest_integer",
|
||||||
"disabled",
|
"statistical_component",
|
||||||
"flexible_benefits",
|
"do_not_include_in_total",
|
||||||
"is_flexible_benefit",
|
"disabled",
|
||||||
"max_benefit_amount",
|
"flexible_benefits",
|
||||||
"column_break_9",
|
"is_flexible_benefit",
|
||||||
"pay_against_benefit_claim",
|
"max_benefit_amount",
|
||||||
"only_tax_impact",
|
"column_break_9",
|
||||||
"create_separate_payment_entry_against_benefit_claim",
|
"pay_against_benefit_claim",
|
||||||
"section_break_11",
|
"only_tax_impact",
|
||||||
"variable_based_on_taxable_salary",
|
"create_separate_payment_entry_against_benefit_claim",
|
||||||
"section_break_5",
|
"section_break_5",
|
||||||
"accounts",
|
"accounts",
|
||||||
"condition_and_formula",
|
"condition_and_formula",
|
||||||
"condition",
|
"condition",
|
||||||
"amount",
|
"amount",
|
||||||
"amount_based_on_formula",
|
"amount_based_on_formula",
|
||||||
"formula",
|
"formula",
|
||||||
"column_break_28",
|
"column_break_28",
|
||||||
"help"
|
"help"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "salary_component",
|
"fieldname": "salary_component",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "salary_component_abbr",
|
"fieldname": "salary_component_abbr",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Abbr",
|
"label": "Abbr",
|
||||||
"print_width": "120px",
|
"print_width": "120px",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"width": "120px"
|
"width": "120px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "type",
|
"fieldname": "type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"options": "Earning\nDeduction",
|
"options": "Earning\nDeduction",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
"depends_on": "eval:doc.type == \"Earning\"",
|
"depends_on": "eval:doc.type == \"Earning\"",
|
||||||
"fieldname": "is_tax_applicable",
|
"fieldname": "is_tax_applicable",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Tax Applicable"
|
"label": "Is Tax Applicable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
"fieldname": "is_payable",
|
"fieldname": "depends_on_payment_days",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Payable"
|
"label": "Depends on Payment Days",
|
||||||
},
|
"print_hide": 1
|
||||||
{
|
},
|
||||||
"default": "1",
|
{
|
||||||
"fieldname": "depends_on_payment_days",
|
"default": "0",
|
||||||
"fieldtype": "Check",
|
"fieldname": "do_not_include_in_total",
|
||||||
"label": "Depends on Payment Days",
|
"fieldtype": "Check",
|
||||||
"print_hide": 1
|
"label": "Do Not Include in Total"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "do_not_include_in_total",
|
"depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'",
|
||||||
"fieldtype": "Check",
|
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||||
"label": "Do Not Include in Total"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Deduct Full Tax on Selected Payroll Date"
|
||||||
{
|
},
|
||||||
"default": "0",
|
{
|
||||||
"depends_on": "is_tax_applicable",
|
"fieldname": "column_break_4",
|
||||||
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
"fieldtype": "Column Break"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"label": "Deduct Full Tax on Selected Payroll Date"
|
{
|
||||||
},
|
"default": "0",
|
||||||
{
|
"fieldname": "disabled",
|
||||||
"fieldname": "column_break_4",
|
"fieldtype": "Check",
|
||||||
"fieldtype": "Column Break"
|
"label": "Disabled"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"fieldname": "description",
|
||||||
"fieldname": "disabled",
|
"fieldtype": "Small Text",
|
||||||
"fieldtype": "Check",
|
"in_list_view": 1,
|
||||||
"label": "Disabled"
|
"label": "Description"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "description",
|
"default": "0",
|
||||||
"fieldtype": "Small Text",
|
"description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
|
||||||
"in_list_view": 1,
|
"fieldname": "statistical_component",
|
||||||
"label": "Description"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Statistical Component"
|
||||||
{
|
},
|
||||||
"default": "0",
|
{
|
||||||
"description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
|
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
|
||||||
"fieldname": "statistical_component",
|
"fieldname": "flexible_benefits",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Section Break",
|
||||||
"label": "Statistical Component"
|
"label": "Flexible Benefits"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
|
"default": "0",
|
||||||
"fieldname": "flexible_benefits",
|
"fieldname": "is_flexible_benefit",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Check",
|
||||||
"label": "Flexible Benefits"
|
"label": "Is Flexible Benefit"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"depends_on": "is_flexible_benefit",
|
||||||
"fieldname": "is_flexible_benefit",
|
"fieldname": "max_benefit_amount",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Currency",
|
||||||
"label": "Is Flexible Benefit"
|
"label": "Max Benefit Amount (Yearly)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "is_flexible_benefit",
|
"fieldname": "column_break_9",
|
||||||
"fieldname": "max_benefit_amount",
|
"fieldtype": "Column Break"
|
||||||
"fieldtype": "Currency",
|
},
|
||||||
"label": "Max Benefit Amount (Yearly)"
|
{
|
||||||
},
|
"default": "0",
|
||||||
{
|
"depends_on": "is_flexible_benefit",
|
||||||
"fieldname": "column_break_9",
|
"fieldname": "pay_against_benefit_claim",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Pay Against Benefit Claim"
|
||||||
{
|
},
|
||||||
"default": "0",
|
{
|
||||||
"depends_on": "is_flexible_benefit",
|
"default": "0",
|
||||||
"fieldname": "pay_against_benefit_claim",
|
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1",
|
||||||
"fieldtype": "Check",
|
"fieldname": "only_tax_impact",
|
||||||
"label": "Pay Against Benefit Claim"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)"
|
||||||
{
|
},
|
||||||
"default": "0",
|
{
|
||||||
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1",
|
"default": "0",
|
||||||
"fieldname": "only_tax_impact",
|
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1",
|
||||||
"fieldtype": "Check",
|
"fieldname": "create_separate_payment_entry_against_benefit_claim",
|
||||||
"label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Create Separate Payment Entry Against Benefit Claim"
|
||||||
{
|
},
|
||||||
"default": "0",
|
{
|
||||||
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1",
|
"default": "0",
|
||||||
"fieldname": "create_separate_payment_entry_against_benefit_claim",
|
"depends_on": "eval:doc.type == \"Deduction\"",
|
||||||
"fieldtype": "Check",
|
"fieldname": "variable_based_on_taxable_salary",
|
||||||
"label": "Create Separate Payment Entry Against Benefit Claim"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Variable Based On Taxable Salary"
|
||||||
{
|
},
|
||||||
"depends_on": "eval:doc.type=='Deduction'",
|
{
|
||||||
"fieldname": "section_break_11",
|
"depends_on": "eval:doc.statistical_component != 1",
|
||||||
"fieldtype": "Section Break"
|
"fieldname": "section_break_5",
|
||||||
},
|
"fieldtype": "Section Break",
|
||||||
{
|
"label": "Accounts"
|
||||||
"default": "0",
|
},
|
||||||
"fieldname": "variable_based_on_taxable_salary",
|
{
|
||||||
"fieldtype": "Check",
|
"fieldname": "accounts",
|
||||||
"label": "Variable Based On Taxable Salary"
|
"fieldtype": "Table",
|
||||||
},
|
"label": "Accounts",
|
||||||
{
|
"options": "Salary Component Account"
|
||||||
"depends_on": "eval:doc.statistical_component != 1",
|
},
|
||||||
"fieldname": "section_break_5",
|
{
|
||||||
"fieldtype": "Section Break",
|
"collapsible": 1,
|
||||||
"label": "Accounts"
|
"depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
|
||||||
},
|
"fieldname": "condition_and_formula",
|
||||||
{
|
"fieldtype": "Section Break",
|
||||||
"fieldname": "accounts",
|
"label": "Condition and Formula"
|
||||||
"fieldtype": "Table",
|
},
|
||||||
"label": "Accounts",
|
{
|
||||||
"options": "Salary Component Account"
|
"fieldname": "condition",
|
||||||
},
|
"fieldtype": "Code",
|
||||||
{
|
"label": "Condition"
|
||||||
"collapsible": 1,
|
},
|
||||||
"depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
|
{
|
||||||
"fieldname": "condition_and_formula",
|
"default": "0",
|
||||||
"fieldtype": "Section Break",
|
"fieldname": "amount_based_on_formula",
|
||||||
"label": "Condition and Formula"
|
"fieldtype": "Check",
|
||||||
},
|
"label": "Amount based on formula"
|
||||||
{
|
},
|
||||||
"fieldname": "condition",
|
{
|
||||||
"fieldtype": "Code",
|
"depends_on": "amount_based_on_formula",
|
||||||
"label": "Condition"
|
"fieldname": "formula",
|
||||||
},
|
"fieldtype": "Code",
|
||||||
{
|
"label": "Formula"
|
||||||
"default": "0",
|
},
|
||||||
"fieldname": "amount_based_on_formula",
|
{
|
||||||
"fieldtype": "Check",
|
"depends_on": "eval:doc.amount_based_on_formula!==1",
|
||||||
"label": "Amount based on formula"
|
"fieldname": "amount",
|
||||||
},
|
"fieldtype": "Currency",
|
||||||
{
|
"label": "Amount"
|
||||||
"depends_on": "amount_based_on_formula",
|
},
|
||||||
"fieldname": "formula",
|
{
|
||||||
"fieldtype": "Code",
|
"fieldname": "column_break_28",
|
||||||
"label": "Formula"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.amount_based_on_formula!==1",
|
"fieldname": "help",
|
||||||
"fieldname": "amount",
|
"fieldtype": "HTML",
|
||||||
"fieldtype": "Currency",
|
"label": "Help",
|
||||||
"label": "Amount"
|
"options": "<h3>Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_28",
|
"default": "0",
|
||||||
"fieldtype": "Column Break"
|
"fieldname": "round_to_the_nearest_integer",
|
||||||
},
|
"fieldtype": "Check",
|
||||||
{
|
"label": "Round to the Nearest Integer"
|
||||||
"fieldname": "help",
|
},
|
||||||
"fieldtype": "HTML",
|
{
|
||||||
"label": "Help",
|
"default": "0",
|
||||||
"options": "<h3>Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
"depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary",
|
||||||
},
|
"description": "If checked, the full amount will be deducted from taxable income before calculating income tax. Otherwise, it can be exempted via Employee Tax Exemption Declaration.",
|
||||||
{
|
"fieldname": "exempted_from_income_tax",
|
||||||
"default": "0",
|
"fieldtype": "Check",
|
||||||
"fieldname": "round_to_the_nearest_integer",
|
"label": "Exempted from Income Tax"
|
||||||
"fieldtype": "Check",
|
}
|
||||||
"label": "Round to the Nearest Integer"
|
],
|
||||||
}
|
"icon": "fa fa-flag",
|
||||||
],
|
"links": [],
|
||||||
"icon": "fa fa-flag",
|
"modified": "2020-04-24 14:50:28.994054",
|
||||||
"modified": "2019-06-05 11:34:14.231228",
|
"modified_by": "Administrator",
|
||||||
"modified_by": "Administrator",
|
"module": "HR",
|
||||||
"module": "HR",
|
"name": "Salary Component",
|
||||||
"name": "Salary Component",
|
"owner": "Administrator",
|
||||||
"owner": "Administrator",
|
"permissions": [
|
||||||
"permissions": [
|
{
|
||||||
{
|
"create": 1,
|
||||||
"create": 1,
|
"delete": 1,
|
||||||
"delete": 1,
|
"email": 1,
|
||||||
"email": 1,
|
"export": 1,
|
||||||
"export": 1,
|
"print": 1,
|
||||||
"print": 1,
|
"read": 1,
|
||||||
"read": 1,
|
"report": 1,
|
||||||
"report": 1,
|
"role": "HR User",
|
||||||
"role": "HR User",
|
"share": 1,
|
||||||
"share": 1,
|
"write": 1
|
||||||
"write": 1
|
},
|
||||||
},
|
{
|
||||||
{
|
"read": 1,
|
||||||
"read": 1,
|
"role": "Employee"
|
||||||
"role": "Employee"
|
}
|
||||||
}
|
],
|
||||||
],
|
"sort_field": "modified",
|
||||||
"sort_field": "modified",
|
"sort_order": "DESC"
|
||||||
"sort_order": "DESC"
|
}
|
||||||
}
|
|
@ -3,14 +3,12 @@
|
|||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"salary_component": "_Test Basic Salary",
|
"salary_component": "_Test Basic Salary",
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"is_payable": 1,
|
|
||||||
"is_tax_applicable": 1
|
"is_tax_applicable": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"salary_component": "_Test Allowance",
|
"salary_component": "_Test Allowance",
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"is_payable": 1,
|
|
||||||
"is_tax_applicable": 1
|
"is_tax_applicable": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -27,14 +25,12 @@
|
|||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"salary_component": "Basic",
|
"salary_component": "Basic",
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"is_payable": 1,
|
|
||||||
"is_tax_applicable": 1
|
"is_tax_applicable": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"salary_component": "Leave Encashment",
|
"salary_component": "Leave Encashment",
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"is_payable": 1,
|
|
||||||
"is_tax_applicable": 1
|
"is_tax_applicable": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -18,6 +18,5 @@ def create_salary_component(component_name, **args):
|
|||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"salary_component": component_name,
|
"salary_component": component_name,
|
||||||
"type": args.get("type") or "Earning",
|
"type": args.get("type") or "Earning",
|
||||||
"is_payable": args.get("is_payable") or 1,
|
|
||||||
"is_tax_applicable": args.get("is_tax_applicable") or 1
|
"is_tax_applicable": args.get("is_tax_applicable") or 1
|
||||||
}).insert()
|
}).insert()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"deduct_full_tax_on_selected_payroll_date",
|
"deduct_full_tax_on_selected_payroll_date",
|
||||||
"depends_on_payment_days",
|
"depends_on_payment_days",
|
||||||
"is_tax_applicable",
|
"is_tax_applicable",
|
||||||
|
"exempted_from_income_tax",
|
||||||
"is_flexible_benefit",
|
"is_flexible_benefit",
|
||||||
"variable_based_on_taxable_salary",
|
"variable_based_on_taxable_salary",
|
||||||
"section_break_2",
|
"section_break_2",
|
||||||
@ -62,6 +63,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.parentfield=='earnings'",
|
||||||
"fetch_from": "salary_component.is_tax_applicable",
|
"fetch_from": "salary_component.is_tax_applicable",
|
||||||
"fieldname": "is_tax_applicable",
|
"fieldname": "is_tax_applicable",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@ -71,6 +73,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.parentfield=='earnings'",
|
||||||
"fetch_from": "salary_component.is_flexible_benefit",
|
"fetch_from": "salary_component.is_flexible_benefit",
|
||||||
"fieldname": "is_flexible_benefit",
|
"fieldname": "is_flexible_benefit",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@ -80,6 +83,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.parentfield=='deductions'",
|
||||||
"fetch_from": "salary_component.variable_based_on_taxable_salary",
|
"fetch_from": "salary_component.variable_based_on_taxable_salary",
|
||||||
"fieldname": "variable_based_on_taxable_salary",
|
"fieldname": "variable_based_on_taxable_salary",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@ -187,11 +191,20 @@
|
|||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"label": "Condition and Formula Help",
|
"label": "Condition and Formula Help",
|
||||||
"options": "<h3>Condition and Formula Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
"options": "<h3>Condition and Formula Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.parentfield=='deductions'",
|
||||||
|
"fetch_from": "salary_component.exempted_from_income_tax",
|
||||||
|
"fieldname": "exempted_from_income_tax",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Exempted from Income Tax",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-31 17:15:25.646689",
|
"modified": "2020-04-24 20:00:16.475295",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
|
@ -51,7 +51,7 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
end_date: function(frm) {
|
end_date: function(frm) {
|
||||||
frm.events.get_emp_and_leave_details(frm);
|
frm.events.get_emp_and_working_day_details(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
set_end_date: function(frm){
|
set_end_date: function(frm){
|
||||||
@ -86,7 +86,7 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
|
|
||||||
salary_slip_based_on_timesheet: function(frm) {
|
salary_slip_based_on_timesheet: function(frm) {
|
||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
frm.events.get_emp_and_leave_details(frm);
|
frm.events.get_emp_and_working_day_details(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
payroll_frequency: function(frm) {
|
payroll_frequency: function(frm) {
|
||||||
@ -95,15 +95,14 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
frm.events.get_emp_and_leave_details(frm);
|
frm.events.get_emp_and_working_day_details(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
leave_without_pay: function(frm){
|
leave_without_pay: function(frm){
|
||||||
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
|
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'process_salary_based_on_leave',
|
method: 'process_salary_based_on_working_days',
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
args: {"lwp": frm.doc.leave_without_pay},
|
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
}
|
}
|
||||||
@ -115,12 +114,12 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
|
frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
|
||||||
|
|
||||||
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
|
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
|
||||||
frm.doc.payroll_frequency!="");
|
frm.doc.payroll_frequency != "");
|
||||||
},
|
},
|
||||||
|
|
||||||
get_emp_and_leave_details: function(frm) {
|
get_emp_and_working_day_details: function(frm) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'get_emp_and_leave_details',
|
method: 'get_emp_and_working_day_details',
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
|
@ -11,20 +11,20 @@
|
|||||||
"employee_name",
|
"employee_name",
|
||||||
"department",
|
"department",
|
||||||
"designation",
|
"designation",
|
||||||
|
"branch",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"company",
|
"status",
|
||||||
"journal_entry",
|
"journal_entry",
|
||||||
"payroll_entry",
|
"payroll_entry",
|
||||||
|
"company",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
"branch",
|
|
||||||
"status",
|
|
||||||
"section_break_10",
|
"section_break_10",
|
||||||
"salary_slip_based_on_timesheet",
|
"salary_slip_based_on_timesheet",
|
||||||
"payroll_frequency",
|
|
||||||
"start_date",
|
"start_date",
|
||||||
"end_date",
|
"end_date",
|
||||||
"column_break_15",
|
"column_break_15",
|
||||||
"salary_structure",
|
"salary_structure",
|
||||||
|
"payroll_frequency",
|
||||||
"total_working_days",
|
"total_working_days",
|
||||||
"leave_without_pay",
|
"leave_without_pay",
|
||||||
"payment_days",
|
"payment_days",
|
||||||
@ -309,6 +309,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "earning",
|
"fieldname": "earning",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"label": "Earning",
|
||||||
"oldfieldtype": "Column Break",
|
"oldfieldtype": "Column Break",
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
@ -323,6 +324,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "deduction",
|
"fieldname": "deduction",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"label": "Deduction",
|
||||||
"oldfieldtype": "Column Break",
|
"oldfieldtype": "Column Break",
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
@ -463,7 +465,7 @@
|
|||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-09 20:02:53.159827",
|
"modified": "2020-04-14 20:02:53.159827",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import datetime, math
|
import datetime, math
|
||||||
|
|
||||||
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words
|
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
|
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
@ -44,9 +44,9 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
if not (len(self.get("earnings")) or len(self.get("deductions"))):
|
if not (len(self.get("earnings")) or len(self.get("deductions"))):
|
||||||
# get details from salary structure
|
# get details from salary structure
|
||||||
self.get_emp_and_leave_details()
|
self.get_emp_and_working_day_details()
|
||||||
else:
|
else:
|
||||||
self.get_leave_details(lwp = self.leave_without_pay)
|
self.get_working_days_details(lwp = self.leave_without_pay)
|
||||||
|
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ class SalarySlip(TransactionBase):
|
|||||||
self.start_date = date_details.start_date
|
self.start_date = date_details.start_date
|
||||||
self.end_date = date_details.end_date
|
self.end_date = date_details.end_date
|
||||||
|
|
||||||
def get_emp_and_leave_details(self):
|
def get_emp_and_working_day_details(self):
|
||||||
'''First time, load all the components from salary structure'''
|
'''First time, load all the components from salary structure'''
|
||||||
if self.employee:
|
if self.employee:
|
||||||
self.set("earnings", [])
|
self.set("earnings", [])
|
||||||
@ -129,7 +129,8 @@ class SalarySlip(TransactionBase):
|
|||||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||||
["date_of_joining", "relieving_date"])
|
["date_of_joining", "relieving_date"])
|
||||||
|
|
||||||
self.get_leave_details(joining_date, relieving_date)
|
#getin leave details
|
||||||
|
self.get_working_days_details(joining_date, relieving_date)
|
||||||
struct = self.check_sal_struct(joining_date, relieving_date)
|
struct = self.check_sal_struct(joining_date, relieving_date)
|
||||||
|
|
||||||
if struct:
|
if struct:
|
||||||
@ -188,10 +189,9 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
make_salary_slip(self._salary_structure_doc.name, self)
|
make_salary_slip(self._salary_structure_doc.name, self)
|
||||||
|
|
||||||
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0):
|
def get_working_days_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0):
|
||||||
if not joining_date:
|
payroll_based_on = frappe.db.get_value("HR Settings", None, "payroll_based_on")
|
||||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
include_holidays_in_total_working_days = frappe.db.get_single_value("HR Settings", "include_holidays_in_total_working_days")
|
||||||
["date_of_joining", "relieving_date"])
|
|
||||||
|
|
||||||
working_days = date_diff(self.end_date, self.start_date) + 1
|
working_days = date_diff(self.end_date, self.start_date) + 1
|
||||||
if for_preview:
|
if for_preview:
|
||||||
@ -200,24 +200,42 @@ class SalarySlip(TransactionBase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
|
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
|
||||||
actual_lwp = self.calculate_lwp(holidays, working_days)
|
|
||||||
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
|
if not cint(include_holidays_in_total_working_days):
|
||||||
working_days -= len(holidays)
|
working_days -= len(holidays)
|
||||||
if working_days < 0:
|
if working_days < 0:
|
||||||
frappe.throw(_("There are more holidays than working days this month."))
|
frappe.throw(_("There are more holidays than working days this month."))
|
||||||
|
|
||||||
|
if not payroll_based_on:
|
||||||
|
frappe.throw(_("Please set Payroll based on in HR settings"))
|
||||||
|
|
||||||
|
if payroll_based_on == "Attendance":
|
||||||
|
actual_lwp = self.calculate_lwp_based_on_attendance(holidays)
|
||||||
|
else:
|
||||||
|
actual_lwp = self.calculate_lwp_based_on_leave_application(holidays, working_days)
|
||||||
|
|
||||||
if not lwp:
|
if not lwp:
|
||||||
lwp = actual_lwp
|
lwp = actual_lwp
|
||||||
elif lwp != actual_lwp:
|
elif lwp != actual_lwp:
|
||||||
frappe.msgprint(_("Leave Without Pay does not match with approved Leave Application records"))
|
frappe.msgprint(_("Leave Without Pay does not match with approved {} records")
|
||||||
|
.format(payroll_based_on))
|
||||||
|
|
||||||
self.total_working_days = working_days
|
|
||||||
self.leave_without_pay = lwp
|
self.leave_without_pay = lwp
|
||||||
|
self.total_working_days = working_days
|
||||||
|
|
||||||
payment_days = flt(self.get_payment_days(joining_date, relieving_date)) - flt(lwp)
|
payment_days = self.get_payment_days(joining_date,
|
||||||
self.payment_days = payment_days > 0 and payment_days or 0
|
relieving_date, include_holidays_in_total_working_days)
|
||||||
|
|
||||||
|
if flt(payment_days) > flt(lwp):
|
||||||
|
self.payment_days = flt(payment_days) - flt(lwp)
|
||||||
|
else:
|
||||||
|
self.payment_days = 0
|
||||||
|
|
||||||
|
def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days):
|
||||||
|
if not joining_date:
|
||||||
|
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||||
|
["date_of_joining", "relieving_date"])
|
||||||
|
|
||||||
def get_payment_days(self, joining_date, relieving_date):
|
|
||||||
start_date = getdate(self.start_date)
|
start_date = getdate(self.start_date)
|
||||||
if joining_date:
|
if joining_date:
|
||||||
if getdate(self.start_date) <= joining_date <= getdate(self.end_date):
|
if getdate(self.start_date) <= joining_date <= getdate(self.end_date):
|
||||||
@ -235,9 +253,10 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
payment_days = date_diff(end_date, start_date) + 1
|
payment_days = date_diff(end_date, start_date) + 1
|
||||||
|
|
||||||
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
|
if not cint(include_holidays_in_total_working_days):
|
||||||
holidays = self.get_holidays_for_employee(start_date, end_date)
|
holidays = self.get_holidays_for_employee(start_date, end_date)
|
||||||
payment_days -= len(holidays)
|
payment_days -= len(holidays)
|
||||||
|
|
||||||
return payment_days
|
return payment_days
|
||||||
|
|
||||||
def get_holidays_for_employee(self, start_date, end_date):
|
def get_holidays_for_employee(self, start_date, end_date):
|
||||||
@ -256,27 +275,67 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
return holidays
|
return holidays
|
||||||
|
|
||||||
def calculate_lwp(self, holidays, working_days):
|
def calculate_lwp_based_on_leave_application(self, holidays, working_days):
|
||||||
lwp = 0
|
lwp = 0
|
||||||
holidays = "','".join(holidays)
|
holidays = "','".join(holidays)
|
||||||
|
daily_wages_fraction_for_half_day = \
|
||||||
|
flt(frappe.db.get_value("HR Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
|
||||||
|
|
||||||
for d in range(working_days):
|
for d in range(working_days):
|
||||||
dt = add_days(cstr(getdate(self.start_date)), d)
|
dt = add_days(cstr(getdate(self.start_date)), d)
|
||||||
leave = frappe.db.sql("""
|
leave = frappe.db.sql("""
|
||||||
SELECT t1.name,
|
SELECT t1.name,
|
||||||
CASE WHEN t1.half_day_date = %(dt)s or t1.to_date = t1.from_date
|
CASE WHEN (t1.half_day_date = %(dt)s or t1.to_date = t1.from_date)
|
||||||
THEN t1.half_day else 0 END
|
THEN t1.half_day else 0 END
|
||||||
FROM `tabLeave Application` t1, `tabLeave Type` t2
|
FROM `tabLeave Application` t1, `tabLeave Type` t2
|
||||||
WHERE t2.name = t1.leave_type
|
WHERE t2.name = t1.leave_type
|
||||||
AND t2.is_lwp = 1
|
AND t2.is_lwp = 1
|
||||||
AND t1.docstatus = 1
|
AND t1.docstatus = 1
|
||||||
AND t1.employee = %(employee)s
|
AND t1.employee = %(employee)s
|
||||||
AND CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date and ifnull(t1.salary_slip, '') = ''
|
AND ifnull(t1.salary_slip, '') = ''
|
||||||
WHEN t2.include_holiday THEN %(dt)s between from_date and to_date and ifnull(t1.salary_slip, '') = ''
|
AND CASE
|
||||||
END
|
WHEN t2.include_holiday != 1
|
||||||
|
THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
|
||||||
|
WHEN t2.include_holiday
|
||||||
|
THEN %(dt)s between from_date and to_date
|
||||||
|
END
|
||||||
""".format(holidays), {"employee": self.employee, "dt": dt})
|
""".format(holidays), {"employee": self.employee, "dt": dt})
|
||||||
|
|
||||||
if leave:
|
if leave:
|
||||||
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
|
is_half_day_leave = cint(leave[0][1])
|
||||||
|
lwp += (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1
|
||||||
|
|
||||||
|
return lwp
|
||||||
|
|
||||||
|
def calculate_lwp_based_on_attendance(self, holidays):
|
||||||
|
lwp = 0
|
||||||
|
|
||||||
|
daily_wages_fraction_for_half_day = \
|
||||||
|
flt(frappe.db.get_value("HR Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
|
||||||
|
|
||||||
|
lwp_leave_types = dict(frappe.get_all("Leave Type", {"is_lwp": 1}, ["name", "include_holiday"], as_list=1))
|
||||||
|
|
||||||
|
attendances = frappe.db.sql('''
|
||||||
|
SELECT attendance_date, status, leave_type
|
||||||
|
FROM `tabAttendance`
|
||||||
|
WHERE
|
||||||
|
status in ("Absent", "Half Day", "On leave")
|
||||||
|
AND employee = %s
|
||||||
|
AND docstatus = 1
|
||||||
|
AND attendance_date between %s and %s
|
||||||
|
''', values=(self.employee, self.start_date, self.end_date), as_dict=1)
|
||||||
|
|
||||||
|
for d in attendances:
|
||||||
|
if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in lwp_leave_types:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if formatdate(d.attendance_date, "yyyy-mm-dd") in holidays:
|
||||||
|
if d.status == "Absent" or \
|
||||||
|
(d.leave_type and d.leave_type in lwp_leave_types and not lwp_leave_types[d.leave_type]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
lwp += (1 - daily_wages_fraction_for_half_day) if d.status == "Half Day" else 1
|
||||||
|
|
||||||
return lwp
|
return lwp
|
||||||
|
|
||||||
def add_earning_for_hourly_wages(self, doc, salary_component, amount):
|
def add_earning_for_hourly_wages(self, doc, salary_component, amount):
|
||||||
@ -451,7 +510,8 @@ class SalarySlip(TransactionBase):
|
|||||||
'is_flexible_benefit': struct_row.is_flexible_benefit,
|
'is_flexible_benefit': struct_row.is_flexible_benefit,
|
||||||
'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
|
'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
|
||||||
'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
|
'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
|
||||||
'additional_amount': amount if struct_row.get("is_additional_component") else 0
|
'additional_amount': amount if struct_row.get("is_additional_component") else 0,
|
||||||
|
'exempted_from_income_tax': struct_row.exempted_from_income_tax
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
if struct_row.get("is_additional_component"):
|
if struct_row.get("is_additional_component"):
|
||||||
@ -482,10 +542,12 @@ class SalarySlip(TransactionBase):
|
|||||||
return self.calculate_variable_tax(payroll_period, tax_component)
|
return self.calculate_variable_tax(payroll_period, tax_component)
|
||||||
|
|
||||||
def calculate_variable_tax(self, payroll_period, tax_component):
|
def calculate_variable_tax(self, payroll_period, tax_component):
|
||||||
|
# get Tax slab from salary structure assignment for the employee and payroll period
|
||||||
|
tax_slab = self.get_income_tax_slabs(payroll_period)
|
||||||
|
|
||||||
# get remaining numbers of sub-period (period for which one salary is processed)
|
# get remaining numbers of sub-period (period for which one salary is processed)
|
||||||
remaining_sub_periods = get_period_factor(self.employee,
|
remaining_sub_periods = get_period_factor(self.employee,
|
||||||
self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
|
self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
|
||||||
|
|
||||||
# get taxable_earnings, paid_taxes for previous period
|
# get taxable_earnings, paid_taxes for previous period
|
||||||
previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date)
|
previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date)
|
||||||
previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component)
|
previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component)
|
||||||
@ -507,23 +569,27 @@ class SalarySlip(TransactionBase):
|
|||||||
unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits
|
unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits
|
||||||
|
|
||||||
# Total exemption amount based on tax exemption declaration
|
# Total exemption amount based on tax exemption declaration
|
||||||
total_exemption_amount, other_incomes = self.get_total_exemption_amount_and_other_incomes(payroll_period)
|
total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab)
|
||||||
|
|
||||||
|
#Employee Other Incomes
|
||||||
|
other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0
|
||||||
|
|
||||||
# Total taxable earnings including additional and other incomes
|
# Total taxable earnings including additional and other incomes
|
||||||
total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \
|
total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \
|
||||||
+ current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount
|
+ current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount
|
||||||
|
|
||||||
# Total taxable earnings without additional earnings with full tax
|
# Total taxable earnings without additional earnings with full tax
|
||||||
total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax
|
total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax
|
||||||
|
|
||||||
# Structured tax amount
|
# Structured tax amount
|
||||||
total_structured_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings_without_full_tax_addl_components)
|
total_structured_tax_amount = self.calculate_tax_by_tax_slab(
|
||||||
|
total_taxable_earnings_without_full_tax_addl_components, tax_slab)
|
||||||
current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
|
current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
|
||||||
|
|
||||||
# Total taxable earnings with additional earnings with full tax
|
# Total taxable earnings with additional earnings with full tax
|
||||||
full_tax_on_additional_earnings = 0.0
|
full_tax_on_additional_earnings = 0.0
|
||||||
if current_additional_earnings_with_full_tax:
|
if current_additional_earnings_with_full_tax:
|
||||||
total_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings)
|
total_tax_amount = self.calculate_tax_by_tax_slab(total_taxable_earnings, tax_slab)
|
||||||
full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount
|
full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount
|
||||||
|
|
||||||
current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings
|
current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings
|
||||||
@ -532,12 +598,30 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
return current_tax_amount
|
return current_tax_amount
|
||||||
|
|
||||||
|
def get_income_tax_slabs(self, payroll_period):
|
||||||
|
income_tax_slab, ss_assignment_name = frappe.db.get_value("Salary Structure Assignment",
|
||||||
|
{"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, ["income_tax_slab", 'name'])
|
||||||
|
|
||||||
|
if not income_tax_slab:
|
||||||
|
frappe.throw(_("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name))
|
||||||
|
|
||||||
|
income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab)
|
||||||
|
if income_tax_slab_doc.disabled:
|
||||||
|
frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab))
|
||||||
|
|
||||||
|
if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date):
|
||||||
|
frappe.throw(_("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}")
|
||||||
|
.format(payroll_period.start_date))
|
||||||
|
|
||||||
|
return income_tax_slab_doc
|
||||||
|
|
||||||
|
|
||||||
def get_taxable_earnings_for_prev_period(self, start_date, end_date):
|
def get_taxable_earnings_for_prev_period(self, start_date, end_date):
|
||||||
taxable_earnings = frappe.db.sql("""
|
taxable_earnings = frappe.db.sql("""
|
||||||
select sum(sd.amount)
|
select sum(sd.amount)
|
||||||
from
|
from
|
||||||
`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
|
`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
|
||||||
where
|
where
|
||||||
sd.parentfield='earnings'
|
sd.parentfield='earnings'
|
||||||
and sd.is_tax_applicable=1
|
and sd.is_tax_applicable=1
|
||||||
and is_flexible_benefit=0
|
and is_flexible_benefit=0
|
||||||
@ -550,7 +634,28 @@ class SalarySlip(TransactionBase):
|
|||||||
"from_date": start_date,
|
"from_date": start_date,
|
||||||
"to_date": end_date
|
"to_date": end_date
|
||||||
})
|
})
|
||||||
return flt(taxable_earnings[0][0]) if taxable_earnings else 0
|
taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0
|
||||||
|
|
||||||
|
exempted_amount = frappe.db.sql("""
|
||||||
|
select sum(sd.amount)
|
||||||
|
from
|
||||||
|
`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
|
||||||
|
where
|
||||||
|
sd.parentfield='deductions'
|
||||||
|
and sd.exempted_from_income_tax=1
|
||||||
|
and is_flexible_benefit=0
|
||||||
|
and ss.docstatus=1
|
||||||
|
and ss.employee=%(employee)s
|
||||||
|
and ss.start_date between %(from_date)s and %(to_date)s
|
||||||
|
and ss.end_date between %(from_date)s and %(to_date)s
|
||||||
|
""", {
|
||||||
|
"employee": self.employee,
|
||||||
|
"from_date": start_date,
|
||||||
|
"to_date": end_date
|
||||||
|
})
|
||||||
|
exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0
|
||||||
|
|
||||||
|
return taxable_earnings - exempted_amount
|
||||||
|
|
||||||
def get_tax_paid_in_period(self, start_date, end_date, tax_component):
|
def get_tax_paid_in_period(self, start_date, end_date, tax_component):
|
||||||
# find total_tax_paid, tax paid for benefit, additional_salary
|
# find total_tax_paid, tax paid for benefit, additional_salary
|
||||||
@ -610,6 +715,13 @@ class SalarySlip(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
taxable_earnings += amount
|
taxable_earnings += amount
|
||||||
|
|
||||||
|
for ded in self.deductions:
|
||||||
|
if ded.exempted_from_income_tax:
|
||||||
|
amount = ded.amount
|
||||||
|
if based_on_payment_days:
|
||||||
|
amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0]
|
||||||
|
taxable_earnings -= flt(amount)
|
||||||
|
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
"taxable_earnings": taxable_earnings,
|
"taxable_earnings": taxable_earnings,
|
||||||
"additional_income": additional_income,
|
"additional_income": additional_income,
|
||||||
@ -672,40 +784,63 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
return total_benefits_paid - total_benefits_claimed
|
return total_benefits_paid - total_benefits_claimed
|
||||||
|
|
||||||
def get_total_exemption_amount_and_other_incomes(self, payroll_period):
|
def get_total_exemption_amount(self, payroll_period, tax_slab):
|
||||||
total_exemption_amount, other_incomes = 0, 0
|
total_exemption_amount = 0
|
||||||
if self.deduct_tax_for_unsubmitted_tax_exemption_proof:
|
if tax_slab.allow_tax_exemption:
|
||||||
exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission",
|
if self.deduct_tax_for_unsubmitted_tax_exemption_proof:
|
||||||
{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
|
exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission",
|
||||||
["exemption_amount", "income_from_other_sources"])
|
{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
|
||||||
if exemption_proof:
|
["exemption_amount"])
|
||||||
total_exemption_amount, other_incomes = exemption_proof
|
if exemption_proof:
|
||||||
else:
|
total_exemption_amount = exemption_proof
|
||||||
declaration = frappe.db.get_value("Employee Tax Exemption Declaration",
|
else:
|
||||||
{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
|
declaration = frappe.db.get_value("Employee Tax Exemption Declaration",
|
||||||
["total_exemption_amount", "income_from_other_sources"])
|
{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
|
||||||
if declaration:
|
["total_exemption_amount"])
|
||||||
total_exemption_amount, other_incomes = declaration
|
if declaration:
|
||||||
|
total_exemption_amount = declaration
|
||||||
|
|
||||||
return total_exemption_amount, other_incomes
|
total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount)
|
||||||
|
|
||||||
def calculate_tax_by_tax_slab(self, payroll_period, annual_taxable_earning):
|
return total_exemption_amount
|
||||||
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
|
|
||||||
annual_taxable_earning -= flt(payroll_period_obj.standard_tax_exemption_amount)
|
def get_income_form_other_sources(self, payroll_period):
|
||||||
|
return frappe.get_all("Employee Other Income",
|
||||||
|
filters={
|
||||||
|
"employee": self.employee,
|
||||||
|
"payroll_period": payroll_period.name,
|
||||||
|
"company": self.company,
|
||||||
|
"docstatus": 1
|
||||||
|
},
|
||||||
|
fields="SUM(amount) as total_amount"
|
||||||
|
)[0].total_amount
|
||||||
|
|
||||||
|
def calculate_tax_by_tax_slab(self, annual_taxable_earning, tax_slab):
|
||||||
data = self.get_data_for_eval()
|
data = self.get_data_for_eval()
|
||||||
data.update({"annual_taxable_earning": annual_taxable_earning})
|
data.update({"annual_taxable_earning": annual_taxable_earning})
|
||||||
taxable_amount = 0
|
tax_amount = 0
|
||||||
for slab in payroll_period_obj.taxable_salary_slabs:
|
for slab in tax_slab.slabs:
|
||||||
if slab.condition and not self.eval_tax_slab_condition(slab.condition, data):
|
if slab.condition and not self.eval_tax_slab_condition(slab.condition, data):
|
||||||
continue
|
continue
|
||||||
if not slab.to_amount and annual_taxable_earning > slab.from_amount:
|
if not slab.to_amount and annual_taxable_earning > slab.from_amount:
|
||||||
taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01
|
tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01
|
||||||
continue
|
continue
|
||||||
if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount:
|
if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount:
|
||||||
taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01
|
tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01
|
||||||
elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount:
|
elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount:
|
||||||
taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01
|
tax_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01
|
||||||
return taxable_amount
|
|
||||||
|
# other taxes and charges on income tax
|
||||||
|
for d in tax_slab.other_taxes_and_charges:
|
||||||
|
if flt(d.min_taxable_income) and flt(d.min_taxable_income) > tax_amount:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount:
|
||||||
|
continue
|
||||||
|
|
||||||
|
tax_amount += tax_amount * flt(d.percent) / 100
|
||||||
|
|
||||||
|
return tax_amount
|
||||||
|
|
||||||
def eval_tax_slab_condition(self, condition, data):
|
def eval_tax_slab_condition(self, condition, data):
|
||||||
try:
|
try:
|
||||||
@ -869,7 +1004,7 @@ class SalarySlip(TransactionBase):
|
|||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
self.get_date_details()
|
self.get_date_details()
|
||||||
self.pull_emp_details()
|
self.pull_emp_details()
|
||||||
self.get_leave_details(for_preview=for_preview)
|
self.get_working_days_details(for_preview=for_preview)
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
def pull_emp_details(self):
|
def pull_emp_details(self):
|
||||||
@ -878,8 +1013,8 @@ class SalarySlip(TransactionBase):
|
|||||||
self.bank_name = emp.bank_name
|
self.bank_name = emp.bank_name
|
||||||
self.bank_account_no = emp.bank_ac_no
|
self.bank_account_no = emp.bank_ac_no
|
||||||
|
|
||||||
def process_salary_based_on_leave(self, lwp=0):
|
def process_salary_based_on_working_days(self):
|
||||||
self.get_leave_details(lwp=lwp)
|
self.get_working_days_details(lwp=self.leave_without_pay)
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
def unlink_ref_doc_from_salary_slip(ref_no):
|
def unlink_ref_doc_from_salary_slip(ref_no):
|
||||||
|
@ -21,18 +21,105 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
make_earning_salary_component(setup=True, company_list=["_Test Company"])
|
make_earning_salary_component(setup=True, company_list=["_Test Company"])
|
||||||
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
|
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
|
||||||
|
|
||||||
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
|
for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance"]:
|
||||||
frappe.db.sql("delete from `tab%s`" % dt)
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
|
|
||||||
self.make_holiday_list()
|
self.make_holiday_list()
|
||||||
|
|
||||||
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
|
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
|
||||||
frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0)
|
frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0)
|
||||||
|
frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None)
|
||||||
|
frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
def test_payment_days_based_on_attendance(self):
|
||||||
|
from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
||||||
|
no_of_days = self.get_no_of_days()
|
||||||
|
|
||||||
|
# Payroll based on attendance
|
||||||
|
frappe.db.set_value("HR Settings", None, "payroll_based_on", "Attendance")
|
||||||
|
frappe.db.set_value("HR Settings", None, "daily_wages_fraction_for_half_day", 0.75)
|
||||||
|
|
||||||
|
emp_id = make_employee("test_for_attendance@salary.com")
|
||||||
|
frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})
|
||||||
|
|
||||||
|
frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
|
||||||
|
|
||||||
|
month_start_date = get_first_day(nowdate())
|
||||||
|
month_end_date = get_last_day(nowdate())
|
||||||
|
|
||||||
|
first_sunday = frappe.db.sql("""
|
||||||
|
select holiday_date from `tabHoliday`
|
||||||
|
where parent = 'Salary Slip Test Holiday List'
|
||||||
|
and holiday_date between %s and %s
|
||||||
|
order by holiday_date
|
||||||
|
""", (month_start_date, month_end_date))[0][0]
|
||||||
|
|
||||||
|
mark_attendance(emp_id, first_sunday, 'Absent', ignore_validate=True) # invalid lwp
|
||||||
|
mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # valid lwp
|
||||||
|
mark_attendance(emp_id, add_days(first_sunday, 2), 'Half Day', leave_type='Leave Without Pay', ignore_validate=True) # valid 0.75 lwp
|
||||||
|
mark_attendance(emp_id, add_days(first_sunday, 3), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # valid lwp
|
||||||
|
mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp
|
||||||
|
mark_attendance(emp_id, add_days(first_sunday, 7), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # invalid lwp
|
||||||
|
|
||||||
|
ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly")
|
||||||
|
|
||||||
|
self.assertEqual(ss.leave_without_pay, 2.25)
|
||||||
|
|
||||||
|
days_in_month = no_of_days[0]
|
||||||
|
no_of_holidays = no_of_days[1]
|
||||||
|
|
||||||
|
self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25)
|
||||||
|
|
||||||
|
#Gross pay calculation based on attendances
|
||||||
|
gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay))
|
||||||
|
|
||||||
|
self.assertEqual(ss.gross_pay, gross_pay)
|
||||||
|
|
||||||
|
frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave")
|
||||||
|
|
||||||
|
def test_payment_days_based_on_leave_application(self):
|
||||||
|
no_of_days = self.get_no_of_days()
|
||||||
|
|
||||||
|
# Payroll based on attendance
|
||||||
|
frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave")
|
||||||
|
|
||||||
|
emp_id = make_employee("test_for_attendance@salary.com")
|
||||||
|
frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})
|
||||||
|
|
||||||
|
frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
|
||||||
|
|
||||||
|
month_start_date = get_first_day(nowdate())
|
||||||
|
month_end_date = get_last_day(nowdate())
|
||||||
|
|
||||||
|
first_sunday = frappe.db.sql("""
|
||||||
|
select holiday_date from `tabHoliday`
|
||||||
|
where parent = 'Salary Slip Test Holiday List'
|
||||||
|
and holiday_date between %s and %s
|
||||||
|
order by holiday_date
|
||||||
|
""", (month_start_date, month_end_date))[0][0]
|
||||||
|
|
||||||
|
make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay")
|
||||||
|
|
||||||
|
ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly")
|
||||||
|
|
||||||
|
self.assertEqual(ss.leave_without_pay, 3)
|
||||||
|
|
||||||
|
days_in_month = no_of_days[0]
|
||||||
|
no_of_holidays = no_of_days[1]
|
||||||
|
|
||||||
|
self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 3)
|
||||||
|
|
||||||
|
#Gross pay calculation based on attendances
|
||||||
|
gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay))
|
||||||
|
|
||||||
|
self.assertEqual(ss.gross_pay, gross_pay)
|
||||||
|
|
||||||
|
frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave")
|
||||||
|
|
||||||
def test_salary_slip_with_holidays_included(self):
|
def test_salary_slip_with_holidays_included(self):
|
||||||
no_of_days = self.get_no_of_days()
|
no_of_days = self.get_no_of_days()
|
||||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1)
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1)
|
||||||
@ -47,10 +134,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
self.assertEqual(ss.payment_days, no_of_days[0])
|
self.assertEqual(ss.payment_days, no_of_days[0])
|
||||||
self.assertEqual(ss.earnings[0].amount, 50000)
|
self.assertEqual(ss.earnings[0].amount, 50000)
|
||||||
self.assertEqual(ss.earnings[1].amount, 3000)
|
self.assertEqual(ss.earnings[1].amount, 3000)
|
||||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
|
||||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
|
||||||
self.assertEqual(ss.gross_pay, 78000)
|
self.assertEqual(ss.gross_pay, 78000)
|
||||||
self.assertEqual(ss.net_pay, 68000.0)
|
|
||||||
|
|
||||||
def test_salary_slip_with_holidays_excluded(self):
|
def test_salary_slip_with_holidays_excluded(self):
|
||||||
no_of_days = self.get_no_of_days()
|
no_of_days = self.get_no_of_days()
|
||||||
@ -67,10 +151,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
self.assertEqual(ss.earnings[0].amount, 50000)
|
self.assertEqual(ss.earnings[0].amount, 50000)
|
||||||
self.assertEqual(ss.earnings[0].default_amount, 50000)
|
self.assertEqual(ss.earnings[0].default_amount, 50000)
|
||||||
self.assertEqual(ss.earnings[1].amount, 3000)
|
self.assertEqual(ss.earnings[1].amount, 3000)
|
||||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
|
||||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
|
||||||
self.assertEqual(ss.gross_pay, 78000)
|
self.assertEqual(ss.gross_pay, 78000)
|
||||||
self.assertEqual(ss.net_pay, 68000.0)
|
|
||||||
|
|
||||||
def test_payment_days(self):
|
def test_payment_days(self):
|
||||||
no_of_days = self.get_no_of_days()
|
no_of_days = self.get_no_of_days()
|
||||||
@ -80,8 +161,8 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
# set joinng date in the same month
|
# set joinng date in the same month
|
||||||
make_employee("test_employee@salary.com")
|
make_employee("test_employee@salary.com")
|
||||||
if getdate(nowdate()).day >= 15:
|
if getdate(nowdate()).day >= 15:
|
||||||
date_of_joining = getdate(add_days(nowdate(),-10))
|
|
||||||
relieving_date = getdate(add_days(nowdate(),-10))
|
relieving_date = getdate(add_days(nowdate(),-10))
|
||||||
|
date_of_joining = getdate(add_days(nowdate(),-10))
|
||||||
elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5:
|
elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5:
|
||||||
date_of_joining = getdate(add_days(nowdate(),-3))
|
date_of_joining = getdate(add_days(nowdate(),-3))
|
||||||
relieving_date = getdate(add_days(nowdate(),-3))
|
relieving_date = getdate(add_days(nowdate(),-3))
|
||||||
@ -131,9 +212,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
def test_email_salary_slip(self):
|
def test_email_salary_slip(self):
|
||||||
frappe.db.sql("delete from `tabEmail Queue`")
|
frappe.db.sql("delete from `tabEmail Queue`")
|
||||||
|
|
||||||
hr_settings = frappe.get_doc("HR Settings", "HR Settings")
|
frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 1)
|
||||||
hr_settings.email_salary_slip_to_employee = 1
|
|
||||||
hr_settings.save()
|
|
||||||
|
|
||||||
make_employee("test_employee@salary.com")
|
make_employee("test_employee@salary.com")
|
||||||
ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
|
ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
|
||||||
@ -203,8 +282,11 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
|
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
|
||||||
frappe.db.sql("""delete from `tabPayroll Period`""")
|
frappe.db.sql("""delete from `tabPayroll Period`""")
|
||||||
frappe.db.sql("""delete from `tabSalary Component`""")
|
frappe.db.sql("""delete from `tabSalary Component`""")
|
||||||
|
|
||||||
payroll_period = create_payroll_period()
|
payroll_period = create_payroll_period()
|
||||||
create_tax_slab(payroll_period)
|
|
||||||
|
create_tax_slab(payroll_period, allow_tax_exemption=True)
|
||||||
|
|
||||||
employee = make_employee("test_tax@salary.slip")
|
employee = make_employee("test_tax@salary.slip")
|
||||||
delete_docs = [
|
delete_docs = [
|
||||||
"Salary Slip",
|
"Salary Slip",
|
||||||
@ -230,8 +312,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
payroll_period, deduct_random=False)
|
payroll_period, deduct_random=False)
|
||||||
tax_paid = get_tax_paid_in_period(employee)
|
tax_paid = get_tax_paid_in_period(employee)
|
||||||
|
|
||||||
# total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200
|
annual_tax = 113589.0
|
||||||
annual_tax = 113568
|
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tax_paid, annual_tax)
|
self.assertEqual(tax_paid, annual_tax)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
@ -255,8 +336,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
# Submit proof for total 120000
|
# Submit proof for total 120000
|
||||||
data["proof-1"] = create_proof_submission(employee, payroll_period, 50000)
|
data["proof"] = create_proof_submission(employee, payroll_period, 120000)
|
||||||
data["proof-2"] = create_proof_submission(employee, payroll_period, 70000)
|
|
||||||
|
|
||||||
# Submit benefit claim for total 50000
|
# Submit benefit claim for total 50000
|
||||||
data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance")
|
data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance")
|
||||||
@ -270,7 +350,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
# total taxable income 416000, 166000 @ 5% ie. 8300
|
# total taxable income 416000, 166000 @ 5% ie. 8300
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tax_paid, 88608)
|
self.assertEqual(tax_paid, 82389.0)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
||||||
raise
|
raise
|
||||||
@ -285,7 +365,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
|
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
|
||||||
tax_paid = get_tax_paid_in_period(employee)
|
tax_paid = get_tax_paid_in_period(employee)
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tax_paid, 121211)
|
self.assertEqual(tax_paid, annual_tax)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
||||||
raise
|
raise
|
||||||
@ -322,11 +402,11 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
return [no_of_days_in_month[1], no_of_holidays_in_month]
|
return [no_of_days_in_month[1], no_of_holidays_in_month]
|
||||||
|
|
||||||
|
|
||||||
def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
|
def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
|
||||||
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure
|
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure
|
||||||
if not salary_structure:
|
if not salary_structure:
|
||||||
salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
|
salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
|
||||||
|
|
||||||
employee = frappe.db.get_value("Employee", {"user_id": user})
|
employee = frappe.db.get_value("Employee", {"user_id": user})
|
||||||
salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee)
|
salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee)
|
||||||
salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
|
salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
|
||||||
@ -456,17 +536,15 @@ def make_deduction_salary_component(setup=False, test_tax=False, company_list=No
|
|||||||
{
|
{
|
||||||
"salary_component": 'Professional Tax',
|
"salary_component": 'Professional Tax',
|
||||||
"abbr":'PT',
|
"abbr":'PT',
|
||||||
"condition": 'base > 10000',
|
|
||||||
"formula": 'base*.1',
|
|
||||||
"type": "Deduction",
|
"type": "Deduction",
|
||||||
"amount_based_on_formula": 1
|
"amount": 200,
|
||||||
|
"exempted_from_income_tax": 1
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"salary_component": 'TDS',
|
"salary_component": 'TDS',
|
||||||
"abbr":'T',
|
"abbr":'T',
|
||||||
"formula": 'base*.1',
|
|
||||||
"type": "Deduction",
|
"type": "Deduction",
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"depends_on_payment_days": 0,
|
"depends_on_payment_days": 0,
|
||||||
"variable_based_on_taxable_salary": 1,
|
"variable_based_on_taxable_salary": 1,
|
||||||
"round_to_the_nearest_integer": 1
|
"round_to_the_nearest_integer": 1
|
||||||
@ -477,9 +555,7 @@ def make_deduction_salary_component(setup=False, test_tax=False, company_list=No
|
|||||||
"salary_component": 'TDS',
|
"salary_component": 'TDS',
|
||||||
"abbr":'T',
|
"abbr":'T',
|
||||||
"condition": 'employment_type=="Intern"',
|
"condition": 'employment_type=="Intern"',
|
||||||
"formula": 'base*.1',
|
|
||||||
"type": "Deduction",
|
"type": "Deduction",
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"round_to_the_nearest_integer": 1
|
"round_to_the_nearest_integer": 1
|
||||||
})
|
})
|
||||||
if setup or test_tax:
|
if setup or test_tax:
|
||||||
@ -535,29 +611,47 @@ def create_benefit_claim(employee, payroll_period, amount, component):
|
|||||||
}).submit()
|
}).submit()
|
||||||
return claim_date
|
return claim_date
|
||||||
|
|
||||||
def create_tax_slab(payroll_period):
|
def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False):
|
||||||
data = [
|
if frappe.db.exists("Income Tax Slab", "Tax Slab: " + payroll_period.name):
|
||||||
|
return
|
||||||
|
|
||||||
|
slabs = [
|
||||||
{
|
{
|
||||||
"from_amount": 250000,
|
"from_amount": 250000,
|
||||||
"to_amount": 500000,
|
"to_amount": 500000,
|
||||||
"percent_deduction": 5.2,
|
"percent_deduction": 5,
|
||||||
"condition": "annual_taxable_earning > 500000"
|
"condition": "annual_taxable_earning > 500000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from_amount": 500001,
|
"from_amount": 500001,
|
||||||
"to_amount": 1000000,
|
"to_amount": 1000000,
|
||||||
"percent_deduction": 20.8
|
"percent_deduction": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from_amount": 1000001,
|
"from_amount": 1000001,
|
||||||
"percent_deduction": 31.2
|
"percent_deduction": 30
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
payroll_period.taxable_salary_slabs = []
|
|
||||||
for item in data:
|
income_tax_slab = frappe.new_doc("Income Tax Slab")
|
||||||
payroll_period.append("taxable_salary_slabs", item)
|
income_tax_slab.name = "Tax Slab: " + payroll_period.name
|
||||||
payroll_period.standard_tax_exemption_amount = 52500
|
income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2)
|
||||||
payroll_period.save()
|
|
||||||
|
if allow_tax_exemption:
|
||||||
|
income_tax_slab.allow_tax_exemption = 1
|
||||||
|
income_tax_slab.standard_tax_exemption_amount = 50000
|
||||||
|
|
||||||
|
for item in slabs:
|
||||||
|
income_tax_slab.append("slabs", item)
|
||||||
|
|
||||||
|
income_tax_slab.append("other_taxes_and_charges", {
|
||||||
|
"description": "cess",
|
||||||
|
"percent": 4
|
||||||
|
})
|
||||||
|
|
||||||
|
income_tax_slab.save()
|
||||||
|
if not dont_submit:
|
||||||
|
income_tax_slab.submit()
|
||||||
|
|
||||||
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
|
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
|
||||||
deducted_dates = []
|
deducted_dates = []
|
||||||
@ -595,3 +689,17 @@ def create_additional_salary(employee, payroll_period, amount):
|
|||||||
"type": "Earning"
|
"type": "Earning"
|
||||||
}).submit()
|
}).submit()
|
||||||
return salary_date
|
return salary_date
|
||||||
|
|
||||||
|
def make_leave_application(employee, from_date, to_date, leave_type, company=None):
|
||||||
|
leave_application = frappe.get_doc(dict(
|
||||||
|
doctype = 'Leave Application',
|
||||||
|
employee = employee,
|
||||||
|
leave_type = leave_type,
|
||||||
|
from_date = from_date,
|
||||||
|
to_date = to_date,
|
||||||
|
company = company or erpnext.get_default_company() or "_Test Company",
|
||||||
|
docstatus = 1,
|
||||||
|
status = "Approved",
|
||||||
|
leave_approver = 'test@example.com'
|
||||||
|
))
|
||||||
|
leave_application.submit()
|
@ -82,6 +82,7 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
{fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")},
|
{fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")},
|
||||||
{fieldname:'base_variable', fieldtype:'Section Break'},
|
{fieldname:'base_variable', fieldtype:'Section Break'},
|
||||||
{fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1},
|
{fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1},
|
||||||
|
{fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'},
|
||||||
{fieldname:'base_col_br', fieldtype:'Column Break'},
|
{fieldname:'base_col_br', fieldtype:'Column Break'},
|
||||||
{fieldname:'base', fieldtype:'Currency', label: __('Base')},
|
{fieldname:'base', fieldtype:'Currency', label: __('Base')},
|
||||||
{fieldname:'variable', fieldtype:'Currency', label: __('Variable')}
|
{fieldname:'variable', fieldtype:'Currency', label: __('Variable')}
|
||||||
|
@ -16,6 +16,7 @@ class SalaryStructure(Document):
|
|||||||
self.validate_amount()
|
self.validate_amount()
|
||||||
self.strip_condition_and_formula_fields()
|
self.strip_condition_and_formula_fields()
|
||||||
self.validate_max_benefits_with_flexi()
|
self.validate_max_benefits_with_flexi()
|
||||||
|
self.validate_component_based_on_tax_slab()
|
||||||
|
|
||||||
def set_missing_values(self):
|
def set_missing_values(self):
|
||||||
overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
|
overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
|
||||||
@ -34,6 +35,12 @@ class SalaryStructure(Document):
|
|||||||
for fieldname in overwritten_fields_if_missing:
|
for fieldname in overwritten_fields_if_missing:
|
||||||
d.set(fieldname, component_default_value.get(fieldname))
|
d.set(fieldname, component_default_value.get(fieldname))
|
||||||
|
|
||||||
|
def validate_component_based_on_tax_slab(self):
|
||||||
|
for row in self.deductions:
|
||||||
|
if row.variable_based_on_taxable_salary and (row.amount or row.formula):
|
||||||
|
frappe.throw(_("Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary")
|
||||||
|
.format(row.idx, row.salary_component))
|
||||||
|
|
||||||
def validate_amount(self):
|
def validate_amount(self):
|
||||||
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
|
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
|
||||||
frappe.throw(_("Net pay cannot be negative"))
|
frappe.throw(_("Net pay cannot be negative"))
|
||||||
@ -82,21 +89,23 @@ class SalaryStructure(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None,
|
def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None,
|
||||||
from_date=None, base=None,variable=None):
|
from_date=None, base=None, variable=None, income_tax_slab=None):
|
||||||
employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee)
|
employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee)
|
||||||
|
|
||||||
if employees:
|
if employees:
|
||||||
if len(employees) > 20:
|
if len(employees) > 20:
|
||||||
frappe.enqueue(assign_salary_structure_for_employees, timeout=600,
|
frappe.enqueue(assign_salary_structure_for_employees, timeout=600,
|
||||||
employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable)
|
employees=employees, salary_structure=self,from_date=from_date,
|
||||||
|
base=base, variable=variable, income_tax_slab=income_tax_slab)
|
||||||
else:
|
else:
|
||||||
assign_salary_structure_for_employees(employees, self, from_date=from_date, base=base,variable=variable)
|
assign_salary_structure_for_employees(employees, self, from_date=from_date,
|
||||||
|
base=base, variable=variable, income_tax_slab=income_tax_slab)
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_("No Employee Found"))
|
frappe.msgprint(_("No Employee Found"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None,variable=None):
|
def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None, variable=None, income_tax_slab=None):
|
||||||
salary_structures_assignments = []
|
salary_structures_assignments = []
|
||||||
existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date)
|
existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date)
|
||||||
count=0
|
count=0
|
||||||
@ -105,7 +114,8 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date
|
|||||||
continue
|
continue
|
||||||
count +=1
|
count +=1
|
||||||
|
|
||||||
salary_structures_assignment = create_salary_structures_assignment(employee, salary_structure, from_date, base, variable)
|
salary_structures_assignment = create_salary_structures_assignment(employee,
|
||||||
|
salary_structure, from_date, base, variable, income_tax_slab)
|
||||||
salary_structures_assignments.append(salary_structures_assignment)
|
salary_structures_assignments.append(salary_structures_assignment)
|
||||||
frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures..."))
|
frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures..."))
|
||||||
|
|
||||||
@ -113,7 +123,7 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date
|
|||||||
frappe.msgprint(_("Structures have been assigned successfully"))
|
frappe.msgprint(_("Structures have been assigned successfully"))
|
||||||
|
|
||||||
|
|
||||||
def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable):
|
def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable, income_tax_slab=None):
|
||||||
assignment = frappe.new_doc("Salary Structure Assignment")
|
assignment = frappe.new_doc("Salary Structure Assignment")
|
||||||
assignment.employee = employee
|
assignment.employee = employee
|
||||||
assignment.salary_structure = salary_structure.name
|
assignment.salary_structure = salary_structure.name
|
||||||
@ -121,6 +131,7 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b
|
|||||||
assignment.from_date = from_date
|
assignment.from_date = from_date
|
||||||
assignment.base = base
|
assignment.base = base
|
||||||
assignment.variable = variable
|
assignment.variable = variable
|
||||||
|
assignment.income_tax_slab = income_tax_slab
|
||||||
assignment.save(ignore_permissions = True)
|
assignment.save(ignore_permissions = True)
|
||||||
assignment.submit()
|
assignment.submit()
|
||||||
return assignment.name
|
return assignment.name
|
||||||
@ -138,7 +149,7 @@ def get_existing_assignments(employees, salary_structure, from_date):
|
|||||||
return salary_structures_assignments
|
return salary_structures_assignments
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
|
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0, ignore_permissions=False):
|
||||||
def postprocess(source, target):
|
def postprocess(source, target):
|
||||||
if employee:
|
if employee:
|
||||||
employee_details = frappe.db.get_value("Employee", employee,
|
employee_details = frappe.db.get_value("Employee", employee,
|
||||||
@ -158,7 +169,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
|
|||||||
"name": "salary_structure"
|
"name": "salary_structure"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, target_doc, postprocess, ignore_child_tables=True)
|
}, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions)
|
||||||
|
|
||||||
if cint(as_print):
|
if cint(as_print):
|
||||||
doc.name = 'Preview for {0}'.format(employee)
|
doc.name = 'Preview for {0}'.format(employee)
|
||||||
|
@ -9,8 +9,9 @@ from frappe.utils.make_random import get_random
|
|||||||
from frappe.utils import nowdate, add_days, add_years, getdate, add_months
|
from frappe.utils import nowdate, add_days, add_years, getdate, add_months
|
||||||
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\
|
from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\
|
||||||
make_deduction_salary_component, make_employee_salary_slip
|
make_deduction_salary_component, make_employee_salary_slip, create_tax_slab
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Fiscal Year"]
|
test_dependencies = ["Fiscal Year"]
|
||||||
@ -70,10 +71,8 @@ class TestSalaryStructure(unittest.TestCase):
|
|||||||
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
|
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
|
||||||
self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
|
self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
|
||||||
self.assertEqual(sal_slip.get("gross_pay"), 78000)
|
self.assertEqual(sal_slip.get("gross_pay"), 78000)
|
||||||
self.assertEqual(sal_slip.get("deductions")[0].amount, 5000)
|
self.assertEqual(sal_slip.get("deductions")[0].amount, 200)
|
||||||
self.assertEqual(sal_slip.get("deductions")[1].amount, 5000)
|
self.assertEqual(sal_slip.get("net_pay"), 78000 - sal_slip.get("total_deduction"))
|
||||||
self.assertEqual(sal_slip.get("total_deduction"), 10000)
|
|
||||||
self.assertEqual(sal_slip.get("net_pay"), 68000)
|
|
||||||
|
|
||||||
def test_whitespaces_in_formula_conditions_fields(self):
|
def test_whitespaces_in_formula_conditions_fields(self):
|
||||||
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
|
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
|
||||||
@ -112,6 +111,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do
|
|||||||
test_tax=False, company=None):
|
test_tax=False, company=None):
|
||||||
if test_tax:
|
if test_tax:
|
||||||
frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
|
frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
|
||||||
|
|
||||||
if not frappe.db.exists('Salary Structure', salary_structure):
|
if not frappe.db.exists('Salary Structure', salary_structure):
|
||||||
details = {
|
details = {
|
||||||
"doctype": "Salary Structure",
|
"doctype": "Salary Structure",
|
||||||
@ -124,7 +124,8 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do
|
|||||||
}
|
}
|
||||||
if other_details and isinstance(other_details, dict):
|
if other_details and isinstance(other_details, dict):
|
||||||
details.update(other_details)
|
details.update(other_details)
|
||||||
salary_structure_doc = frappe.get_doc(details).insert()
|
salary_structure_doc = frappe.get_doc(details)
|
||||||
|
salary_structure_doc.insert()
|
||||||
if not dont_submit:
|
if not dont_submit:
|
||||||
salary_structure_doc.submit()
|
salary_structure_doc.submit()
|
||||||
else:
|
else:
|
||||||
@ -139,13 +140,18 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do
|
|||||||
def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None):
|
def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None):
|
||||||
if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
|
if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
|
||||||
frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee))
|
frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee))
|
||||||
|
|
||||||
|
payroll_period = create_payroll_period()
|
||||||
|
create_tax_slab(payroll_period, allow_tax_exemption=True)
|
||||||
|
|
||||||
salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
|
salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
|
||||||
salary_structure_assignment.employee = employee
|
salary_structure_assignment.employee = employee
|
||||||
salary_structure_assignment.base = 50000
|
salary_structure_assignment.base = 50000
|
||||||
salary_structure_assignment.variable = 5000
|
salary_structure_assignment.variable = 5000
|
||||||
salary_structure_assignment.from_date = from_date or add_months(nowdate(), -1)
|
salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1)
|
||||||
salary_structure_assignment.salary_structure = salary_structure
|
salary_structure_assignment.salary_structure = salary_structure
|
||||||
salary_structure_assignment.company = company or erpnext.get_default_company()
|
salary_structure_assignment.company = company or erpnext.get_default_company()
|
||||||
salary_structure_assignment.save(ignore_permissions=True)
|
salary_structure_assignment.save(ignore_permissions=True)
|
||||||
|
salary_structure_assignment.income_tax_slab = "Tax Slab: _Test Payroll Period"
|
||||||
salary_structure_assignment.submit()
|
salary_structure_assignment.submit()
|
||||||
return salary_structure_assignment
|
return salary_structure_assignment
|
||||||
|
@ -20,6 +20,16 @@ frappe.ui.form.on('Salary Structure Assignment', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("income_tax_slab", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company,
|
||||||
|
docstatus: 1,
|
||||||
|
disabled: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
if(frm.doc.employee){
|
if(frm.doc.employee){
|
||||||
|
@ -10,11 +10,12 @@
|
|||||||
"employee",
|
"employee",
|
||||||
"employee_name",
|
"employee_name",
|
||||||
"department",
|
"department",
|
||||||
"designation",
|
"company",
|
||||||
"column_break_6",
|
"column_break_6",
|
||||||
|
"designation",
|
||||||
"salary_structure",
|
"salary_structure",
|
||||||
"from_date",
|
"from_date",
|
||||||
"company",
|
"income_tax_slab",
|
||||||
"section_break_7",
|
"section_break_7",
|
||||||
"base",
|
"base",
|
||||||
"column_break_9",
|
"column_break_9",
|
||||||
@ -113,11 +114,17 @@
|
|||||||
"options": "Salary Structure Assignment",
|
"options": "Salary Structure Assignment",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "income_tax_slab",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Income Tax Slab",
|
||||||
|
"options": "Income Tax Slab"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-31 16:35:34.415099",
|
"modified": "2020-04-25 18:24:23.617088",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Structure Assignment",
|
"name": "Salary Structure Assignment",
|
||||||
|
@ -1,85 +1,186 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from erpnext.hr.doctype.leave_application.leave_application \
|
from frappe import _
|
||||||
import get_leave_balance_on, get_leaves_for_period
|
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on, get_leave_allocation_records
|
||||||
|
|
||||||
from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary \
|
|
||||||
import get_department_leave_approver_map
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
|
if filters.to_date <= filters.from_date:
|
||||||
|
frappe.throw(_('From date can not be greater than than To date'))
|
||||||
|
|
||||||
columns = get_columns(leave_types)
|
columns = get_columns()
|
||||||
data = get_data(filters, leave_types)
|
data = get_data(filters)
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns(leave_types):
|
def get_columns():
|
||||||
columns = [
|
columns = [{
|
||||||
_("Employee") + ":Link.Employee:150",
|
'label': _('Leave Type'),
|
||||||
_("Employee Name") + "::200",
|
'fieldtype': 'Link',
|
||||||
_("Department") +"::150"
|
'fieldname': 'leave_type',
|
||||||
]
|
'width': 200,
|
||||||
|
'options': 'Leave Type'
|
||||||
for leave_type in leave_types:
|
}, {
|
||||||
columns.append(_(leave_type) + " " + _("Opening") + ":Float:160")
|
'label': _('Employee'),
|
||||||
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
|
'fieldtype': 'Link',
|
||||||
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
|
'fieldname': 'employee',
|
||||||
|
'width': 100,
|
||||||
|
'options': 'Employee'
|
||||||
|
}, {
|
||||||
|
'label': _('Employee Name'),
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'fieldname': 'employee_name',
|
||||||
|
'width': 100,
|
||||||
|
}, {
|
||||||
|
'label': _('Opening Balance'),
|
||||||
|
'fieldtype': 'float',
|
||||||
|
'fieldname': 'opening_balance',
|
||||||
|
'width': 130,
|
||||||
|
}, {
|
||||||
|
'label': _('Leaves Allocated'),
|
||||||
|
'fieldtype': 'float',
|
||||||
|
'fieldname': 'leaves_allocated',
|
||||||
|
'width': 130,
|
||||||
|
}, {
|
||||||
|
'label': _('Leaves Taken'),
|
||||||
|
'fieldtype': 'float',
|
||||||
|
'fieldname': 'leaves_taken',
|
||||||
|
'width': 130,
|
||||||
|
}, {
|
||||||
|
'label': _('Leaves Expired'),
|
||||||
|
'fieldtype': 'float',
|
||||||
|
'fieldname': 'leaves_expired',
|
||||||
|
'width': 130,
|
||||||
|
}, {
|
||||||
|
'label': _('Closing Balance'),
|
||||||
|
'fieldtype': 'float',
|
||||||
|
'fieldname': 'closing_balance',
|
||||||
|
'width': 130,
|
||||||
|
}]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_conditions(filters):
|
def get_data(filters):
|
||||||
conditions = {
|
leave_types = frappe.db.sql_list("SELECT `name` FROM `tabLeave Type` ORDER BY `name` ASC")
|
||||||
"status": "Active",
|
|
||||||
"company": filters.company,
|
|
||||||
}
|
|
||||||
if filters.get("department"):
|
|
||||||
conditions.update({"department": filters.get("department")})
|
|
||||||
if filters.get("employee"):
|
|
||||||
conditions.update({"employee": filters.get("employee")})
|
|
||||||
|
|
||||||
return conditions
|
|
||||||
|
|
||||||
def get_data(filters, leave_types):
|
|
||||||
user = frappe.session.user
|
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
|
|
||||||
if filters.to_date <= filters.from_date:
|
user = frappe.session.user
|
||||||
frappe.throw(_("From date can not be greater than than To date"))
|
|
||||||
|
|
||||||
active_employees = frappe.get_all("Employee",
|
|
||||||
filters=conditions,
|
|
||||||
fields=["name", "employee_name", "department", "user_id", "leave_approver"])
|
|
||||||
|
|
||||||
department_approver_map = get_department_leave_approver_map(filters.get('department'))
|
department_approver_map = get_department_leave_approver_map(filters.get('department'))
|
||||||
|
|
||||||
|
active_employees = frappe.get_list('Employee',
|
||||||
|
filters=conditions,
|
||||||
|
fields=['name', 'employee_name', 'department', 'user_id', 'leave_approver'])
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for employee in active_employees:
|
|
||||||
leave_approvers = department_approver_map.get(employee.department_name, [])
|
|
||||||
if employee.leave_approver:
|
|
||||||
leave_approvers.append(employee.leave_approver)
|
|
||||||
|
|
||||||
if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)):
|
for leave_type in leave_types:
|
||||||
row = [employee.name, employee.employee_name, employee.department]
|
if len(active_employees) > 1:
|
||||||
|
data.append({
|
||||||
|
'leave_type': leave_type
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
row = frappe._dict({
|
||||||
|
'leave_type': leave_type
|
||||||
|
})
|
||||||
|
|
||||||
|
for employee in active_employees:
|
||||||
|
|
||||||
|
leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver)
|
||||||
|
|
||||||
|
if (leave_approvers and len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) \
|
||||||
|
or ("HR Manager" in frappe.get_roles(user)):
|
||||||
|
if len(active_employees) > 1:
|
||||||
|
row = frappe._dict()
|
||||||
|
row.employee = employee.name,
|
||||||
|
row.employee_name = employee.employee_name
|
||||||
|
|
||||||
for leave_type in leave_types:
|
|
||||||
# leaves taken
|
|
||||||
leaves_taken = get_leaves_for_period(employee.name, leave_type,
|
leaves_taken = get_leaves_for_period(employee.name, leave_type,
|
||||||
filters.from_date, filters.to_date) * -1
|
filters.from_date, filters.to_date) * -1
|
||||||
|
|
||||||
# opening balance
|
new_allocation, expired_leaves = get_allocated_and_expired_leaves(filters.from_date, filters.to_date, employee.name, leave_type)
|
||||||
|
|
||||||
|
|
||||||
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
||||||
|
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date)
|
||||||
|
|
||||||
# closing balance
|
row.leaves_allocated = new_allocation
|
||||||
closing = max(opening - leaves_taken, 0)
|
row.leaves_expired = expired_leaves - leaves_taken if expired_leaves - leaves_taken > 0 else 0
|
||||||
|
row.opening_balance = opening
|
||||||
|
row.leaves_taken = leaves_taken
|
||||||
|
row.closing_balance = closing
|
||||||
|
row.indent = 1
|
||||||
|
data.append(row)
|
||||||
|
new_leaves_allocated = 0
|
||||||
|
|
||||||
row += [opening, leaves_taken, closing]
|
|
||||||
|
|
||||||
data.append(row)
|
return data
|
||||||
|
|
||||||
return data
|
def get_conditions(filters):
|
||||||
|
conditions={
|
||||||
|
'status': 'Active',
|
||||||
|
}
|
||||||
|
if filters.get('employee'):
|
||||||
|
conditions['name'] = filters.get('employee')
|
||||||
|
|
||||||
|
if filters.get('employee'):
|
||||||
|
conditions['name'] = filters.get('employee')
|
||||||
|
|
||||||
|
return conditions
|
||||||
|
|
||||||
|
def get_department_leave_approver_map(department=None):
|
||||||
|
conditions=''
|
||||||
|
if department:
|
||||||
|
conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department}
|
||||||
|
|
||||||
|
# get current department and all its child
|
||||||
|
department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec
|
||||||
|
|
||||||
|
# retrieve approvers list from current department and from its subsequent child departments
|
||||||
|
approver_list = frappe.get_all('Department Approver', filters={
|
||||||
|
'parentfield': 'leave_approvers',
|
||||||
|
'parent': ('in', department_list)
|
||||||
|
}, fields=['parent', 'approver'], as_list=1)
|
||||||
|
|
||||||
|
approvers = {}
|
||||||
|
|
||||||
|
for k, v in approver_list:
|
||||||
|
approvers.setdefault(k, []).append(v)
|
||||||
|
|
||||||
|
return approvers
|
||||||
|
|
||||||
|
def get_allocated_and_expired_leaves(from_date, to_date, employee, leave_type):
|
||||||
|
|
||||||
|
from frappe.utils import getdate
|
||||||
|
|
||||||
|
new_allocation = 0
|
||||||
|
expired_leaves = 0
|
||||||
|
|
||||||
|
records= frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
employee, leave_type, from_date, to_date, leaves, transaction_name,
|
||||||
|
is_carry_forward, is_expired
|
||||||
|
FROM `tabLeave Ledger Entry`
|
||||||
|
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
||||||
|
AND docstatus=1 AND leaves>0
|
||||||
|
AND (from_date between %(from_date)s AND %(to_date)s
|
||||||
|
OR to_date between %(from_date)s AND %(to_date)s
|
||||||
|
OR (from_date < %(from_date)s AND to_date > %(to_date)s))
|
||||||
|
""", {
|
||||||
|
"from_date": from_date,
|
||||||
|
"to_date": to_date,
|
||||||
|
"employee": employee,
|
||||||
|
"leave_type": leave_type
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
|
for record in records:
|
||||||
|
if record.to_date <= getdate(to_date):
|
||||||
|
expired_leaves += record.leaves
|
||||||
|
|
||||||
|
if record.from_date >= getdate(from_date):
|
||||||
|
new_allocation += record.leaves
|
||||||
|
|
||||||
|
return new_allocation, expired_leaves
|
||||||
|
@ -5,18 +5,11 @@
|
|||||||
frappe.query_reports['Employee Leave Balance Summary'] = {
|
frappe.query_reports['Employee Leave Balance Summary'] = {
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
fieldname:'from_date',
|
fieldname:'date',
|
||||||
label: __('From Date'),
|
label: __('Date'),
|
||||||
fieldtype: 'Date',
|
fieldtype: 'Date',
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
default: frappe.defaults.get_default('year_start_date')
|
default: frappe.datetime.now_date()
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldname:'to_date',
|
|
||||||
label: __('To Date'),
|
|
||||||
fieldtype: 'Date',
|
|
||||||
reqd: 1,
|
|
||||||
default: frappe.defaults.get_default('year_end_date')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname:'company',
|
fieldname:'company',
|
||||||
|
@ -1,130 +1,75 @@
|
|||||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# For license information, please see license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on
|
from frappe.utils import flt
|
||||||
|
from erpnext.hr.doctype.leave_application.leave_application \
|
||||||
|
import get_leave_balance_on, get_leaves_for_period
|
||||||
|
|
||||||
|
from erpnext.hr.report.employee_leave_balance.employee_leave_balance \
|
||||||
|
import get_department_leave_approver_map
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if filters.to_date <= filters.from_date:
|
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
|
||||||
frappe.throw(_('From date can not be greater than than To date'))
|
|
||||||
|
|
||||||
columns = get_columns()
|
columns = get_columns(leave_types)
|
||||||
data = get_data(filters)
|
data = get_data(filters, leave_types)
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns():
|
def get_columns(leave_types):
|
||||||
columns = [{
|
columns = [
|
||||||
'label': _('Leave Type'),
|
_("Employee") + ":Link.Employee:150",
|
||||||
'fieldtype': 'Link',
|
_("Employee Name") + "::200",
|
||||||
'fieldname': 'leave_type',
|
_("Department") +"::150"
|
||||||
'width': 300,
|
]
|
||||||
'options': 'Leave Type'
|
|
||||||
}, {
|
for leave_type in leave_types:
|
||||||
'label': _('Employee'),
|
columns.append(_(leave_type) + ":Float:160")
|
||||||
'fieldtype': 'Link',
|
|
||||||
'fieldname': 'employee',
|
|
||||||
'width': 100,
|
|
||||||
'options': 'Employee'
|
|
||||||
}, {
|
|
||||||
'label': _('Employee Name'),
|
|
||||||
'fieldtype': 'Data',
|
|
||||||
'fieldname': 'employee_name',
|
|
||||||
'width': 100,
|
|
||||||
}, {
|
|
||||||
'label': _('Opening Balance'),
|
|
||||||
'fieldtype': 'float',
|
|
||||||
'fieldname': 'opening_balance',
|
|
||||||
'width': 160,
|
|
||||||
}, {
|
|
||||||
'label': _('Leaves Taken'),
|
|
||||||
'fieldtype': 'float',
|
|
||||||
'fieldname': 'leaves_taken',
|
|
||||||
'width': 160,
|
|
||||||
}, {
|
|
||||||
'label': _('Closing Balance'),
|
|
||||||
'fieldtype': 'float',
|
|
||||||
'fieldname': 'closing_balance',
|
|
||||||
'width': 160,
|
|
||||||
}]
|
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_data(filters):
|
|
||||||
leave_types = frappe.db.sql_list("SELECT `name` FROM `tabLeave Type` ORDER BY `name` ASC")
|
|
||||||
|
|
||||||
conditions = get_conditions(filters)
|
|
||||||
|
|
||||||
user = frappe.session.user
|
|
||||||
department_approver_map = get_department_leave_approver_map(filters.get('department'))
|
|
||||||
|
|
||||||
active_employees = frappe.get_list('Employee',
|
|
||||||
filters=conditions,
|
|
||||||
fields=['name', 'employee_name', 'department', 'user_id', 'leave_approver'])
|
|
||||||
|
|
||||||
data = []
|
|
||||||
|
|
||||||
for leave_type in leave_types:
|
|
||||||
data.append({
|
|
||||||
'leave_type': leave_type
|
|
||||||
})
|
|
||||||
for employee in active_employees:
|
|
||||||
|
|
||||||
leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver)
|
|
||||||
|
|
||||||
if (leave_approvers and len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) \
|
|
||||||
or ("HR Manager" in frappe.get_roles(user)):
|
|
||||||
row = frappe._dict({
|
|
||||||
'employee': employee.name,
|
|
||||||
'employee_name': employee.employee_name
|
|
||||||
})
|
|
||||||
|
|
||||||
leaves_taken = get_leaves_for_period(employee.name, leave_type,
|
|
||||||
filters.from_date, filters.to_date) * -1
|
|
||||||
|
|
||||||
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
|
||||||
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date)
|
|
||||||
|
|
||||||
row.opening_balance = opening
|
|
||||||
row.leaves_taken = leaves_taken
|
|
||||||
row.closing_balance = closing
|
|
||||||
row.indent = 1
|
|
||||||
data.append(row)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_conditions(filters):
|
def get_conditions(filters):
|
||||||
conditions={
|
conditions = {
|
||||||
'status': 'Active',
|
"status": "Active",
|
||||||
|
"company": filters.company,
|
||||||
}
|
}
|
||||||
if filters.get('employee'):
|
if filters.get("department"):
|
||||||
conditions['name'] = filters.get('employee')
|
conditions.update({"department": filters.get("department")})
|
||||||
|
if filters.get("employee"):
|
||||||
if filters.get('employee'):
|
conditions.update({"employee": filters.get("employee")})
|
||||||
conditions['name'] = filters.get('employee')
|
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_department_leave_approver_map(department=None):
|
def get_data(filters, leave_types):
|
||||||
conditions=''
|
user = frappe.session.user
|
||||||
if department:
|
conditions = get_conditions(filters)
|
||||||
conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department}
|
|
||||||
|
|
||||||
# get current department and all its child
|
active_employees = frappe.get_all("Employee",
|
||||||
department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec
|
filters=conditions,
|
||||||
|
fields=["name", "employee_name", "department", "user_id", "leave_approver"])
|
||||||
|
|
||||||
# retrieve approvers list from current department and from its subsequent child departments
|
department_approver_map = get_department_leave_approver_map(filters.get('department'))
|
||||||
approver_list = frappe.get_all('Department Approver', filters={
|
|
||||||
'parentfield': 'leave_approvers',
|
|
||||||
'parent': ('in', department_list)
|
|
||||||
}, fields=['parent', 'approver'], as_list=1)
|
|
||||||
|
|
||||||
approvers = {}
|
data = []
|
||||||
|
for employee in active_employees:
|
||||||
|
leave_approvers = department_approver_map.get(employee.department_name, [])
|
||||||
|
if employee.leave_approver:
|
||||||
|
leave_approvers.append(employee.leave_approver)
|
||||||
|
|
||||||
for k, v in approver_list:
|
if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)):
|
||||||
approvers.setdefault(k, []).append(v)
|
row = [employee.name, employee.employee_name, employee.department]
|
||||||
|
|
||||||
return approvers
|
for leave_type in leave_types:
|
||||||
|
|
||||||
|
# opening balance
|
||||||
|
opening = get_leave_balance_on(employee.name, leave_type, filters.date)
|
||||||
|
|
||||||
|
|
||||||
|
row += [opening]
|
||||||
|
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
|
return data
|
@ -9,6 +9,8 @@ from frappe.model.document import Document
|
|||||||
from frappe.desk.form import assign_to
|
from frappe.desk.form import assign_to
|
||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
|
|
||||||
|
class DuplicateDeclarationError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class EmployeeBoardingController(Document):
|
class EmployeeBoardingController(Document):
|
||||||
'''
|
'''
|
||||||
Create the project and the task for the boarding process
|
Create the project and the task for the boarding process
|
||||||
@ -226,6 +228,17 @@ def get_employee_leave_policy(employee):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("Please set leave policy for employee {0} in Employee / Grade record").format(employee))
|
frappe.throw(_("Please set leave policy for employee {0} in Employee / Grade record").format(employee))
|
||||||
|
|
||||||
|
def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee):
|
||||||
|
existing_record = frappe.db.exists(doctype, {
|
||||||
|
"payroll_period": payroll_period,
|
||||||
|
"employee": employee,
|
||||||
|
'docstatus': ['<', 2],
|
||||||
|
'name': ['!=', docname]
|
||||||
|
})
|
||||||
|
if existing_record:
|
||||||
|
frappe.throw(_("{0} already exists for employee {1} and period {2}")
|
||||||
|
.format(doctype, employee, payroll_period), DuplicateDeclarationError)
|
||||||
|
|
||||||
def validate_tax_declaration(declarations):
|
def validate_tax_declaration(declarations):
|
||||||
subcategories = []
|
subcategories = []
|
||||||
for d in declarations:
|
for d in declarations:
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
"autoname": "field:loan_security_name",
|
"autoname": "field:loan_security_name",
|
||||||
"creation": "2019-09-02 15:07:08.885593",
|
"creation": "2019-09-02 15:07:08.885593",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"loan_security_type",
|
|
||||||
"loan_security_code",
|
|
||||||
"loan_security_name",
|
"loan_security_name",
|
||||||
"unit_of_measure",
|
"unit_of_measure",
|
||||||
|
"loan_security_code",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
|
"loan_security_type",
|
||||||
"haircut",
|
"haircut",
|
||||||
"disabled"
|
"disabled"
|
||||||
],
|
],
|
||||||
@ -17,7 +19,9 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "loan_security_name",
|
"fieldname": "loan_security_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Loan Security Name",
|
"label": "Loan Security Name",
|
||||||
|
"reqd": 1,
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -33,8 +37,10 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "loan_security_type",
|
"fieldname": "loan_security_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Loan Security Type",
|
"label": "Loan Security Type",
|
||||||
"options": "Loan Security Type"
|
"options": "Loan Security Type",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "loan_security_code",
|
"fieldname": "loan_security_code",
|
||||||
@ -52,11 +58,15 @@
|
|||||||
"fetch_from": "loan_security_type.unit_of_measure",
|
"fetch_from": "loan_security_type.unit_of_measure",
|
||||||
"fieldname": "unit_of_measure",
|
"fieldname": "unit_of_measure",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Unit Of Measure",
|
"label": "Unit Of Measure",
|
||||||
"options": "UOM"
|
"options": "UOM",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-11-16 11:36:37.901656",
|
"links": [],
|
||||||
|
"modified": "2020-04-28 14:07:54.506896",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Security",
|
"name": "Loan Security",
|
||||||
@ -87,7 +97,6 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
|
||||||
"search_fields": "loan_security_code",
|
"search_fields": "loan_security_code",
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
"loan_security_type",
|
"loan_security_type",
|
||||||
"unit_of_measure",
|
"unit_of_measure",
|
||||||
"haircut",
|
"haircut",
|
||||||
"disabled",
|
|
||||||
"column_break_5",
|
"column_break_5",
|
||||||
"loan_to_value_ratio"
|
"loan_to_value_ratio",
|
||||||
|
"disabled"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -23,7 +23,9 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "loan_security_type",
|
"fieldname": "loan_security_type",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Loan Security Type",
|
"label": "Loan Security Type",
|
||||||
|
"reqd": 1,
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -34,8 +36,10 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "unit_of_measure",
|
"fieldname": "unit_of_measure",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Unit Of Measure",
|
"label": "Unit Of Measure",
|
||||||
"options": "UOM"
|
"options": "UOM",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_5",
|
"fieldname": "column_break_5",
|
||||||
@ -48,7 +52,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-02-28 12:43:20.364447",
|
"modified": "2020-04-28 14:06:49.046177",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Security Type",
|
"name": "Loan Security Type",
|
||||||
|
@ -6,10 +6,17 @@ def get_data():
|
|||||||
'heatmap': True,
|
'heatmap': True,
|
||||||
'heatmap_message': _('Member Activity'),
|
'heatmap_message': _('Member Activity'),
|
||||||
'fieldname': 'member',
|
'fieldname': 'member',
|
||||||
|
'non_standard_fieldnames': {
|
||||||
|
'Bank Account': 'party'
|
||||||
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Membership Details'),
|
'label': _('Membership Details'),
|
||||||
'items': ['Membership']
|
'items': ['Membership']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Fee'),
|
||||||
|
'items': ['Bank Account']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -661,6 +661,7 @@ erpnext.patches.v12_0.set_job_offer_applicant_email
|
|||||||
erpnext.patches.v12_0.create_irs_1099_field_united_states
|
erpnext.patches.v12_0.create_irs_1099_field_united_states
|
||||||
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
|
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
|
||||||
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
|
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
|
||||||
|
erpnext.patches.v12_0.add_permission_in_lower_deduction
|
||||||
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
|
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
|
||||||
erpnext.patches.v12_0.rename_account_type_doctype
|
erpnext.patches.v12_0.rename_account_type_doctype
|
||||||
erpnext.patches.v12_0.recalculate_requested_qty_in_bin
|
erpnext.patches.v12_0.recalculate_requested_qty_in_bin
|
||||||
@ -668,5 +669,9 @@ erpnext.patches.v12_0.update_healthcare_refactored_changes
|
|||||||
erpnext.patches.v12_0.set_total_batch_quantity
|
erpnext.patches.v12_0.set_total_batch_quantity
|
||||||
erpnext.patches.v12_0.rename_mws_settings_fields
|
erpnext.patches.v12_0.rename_mws_settings_fields
|
||||||
erpnext.patches.v12_0.set_updated_purpose_in_pick_list
|
erpnext.patches.v12_0.set_updated_purpose_in_pick_list
|
||||||
|
erpnext.patches.v12_0.set_default_payroll_based_on
|
||||||
erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse
|
erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse
|
||||||
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
|
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
|
||||||
|
erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
|
||||||
|
erpnext.patches.v12_0.fix_quotation_expired_status
|
||||||
|
erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
|
@ -6,4 +6,5 @@ def execute():
|
|||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc("regional", "doctype", "lower_deduction_certificate")
|
||||||
add_permissions()
|
add_permissions()
|
@ -5,8 +5,7 @@ def execute():
|
|||||||
frappe.reload_doc('hr', 'doctype', 'salary_detail')
|
frappe.reload_doc('hr', 'doctype', 'salary_detail')
|
||||||
frappe.reload_doc('hr', 'doctype', 'salary_component')
|
frappe.reload_doc('hr', 'doctype', 'salary_component')
|
||||||
|
|
||||||
frappe.db.sql("update `tabSalary Component` set is_payable=1, is_tax_applicable=1 where type='Earning'")
|
frappe.db.sql("update `tabSalary Component` set is_tax_applicable=1 where type='Earning'")
|
||||||
frappe.db.sql("update `tabSalary Component` set is_payable=0 where type='Deduction'")
|
|
||||||
|
|
||||||
frappe.db.sql("""update `tabSalary Component` set variable_based_on_taxable_salary=1
|
frappe.db.sql("""update `tabSalary Component` set variable_based_on_taxable_salary=1
|
||||||
where type='Deduction' and name in ('TDS', 'Tax Deducted at Source')""")
|
where type='Deduction' and name in ('TDS', 'Tax Deducted at Source')""")
|
||||||
|
13
erpnext/patches/v12_0/add_permission_in_lower_deduction.py
Normal file
13
erpnext/patches/v12_0/add_permission_in_lower_deduction.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.permissions import add_permission, update_permission_property
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc('regional', 'doctype', 'Lower Deduction Certificate')
|
||||||
|
|
||||||
|
add_permission('Lower Deduction Certificate', 'Accounts Manager', 0)
|
||||||
|
update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'write', 1)
|
||||||
|
update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'create', 1)
|
34
erpnext/patches/v12_0/fix_quotation_expired_status.py
Normal file
34
erpnext/patches/v12_0/fix_quotation_expired_status.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
# fixes status of quotations which have status 'Expired' despite having valid sales order created
|
||||||
|
|
||||||
|
# filter out submitted expired quotations which has sales order created
|
||||||
|
cond = "qo.docstatus = 1 and qo.status = 'Expired'"
|
||||||
|
invalid_so_against_quo = """
|
||||||
|
SELECT
|
||||||
|
so.name FROM `tabSales Order` so, `tabSales Order Item` so_item
|
||||||
|
WHERE
|
||||||
|
so_item.docstatus = 1 and so.docstatus = 1
|
||||||
|
and so_item.parent = so.name
|
||||||
|
and so_item.prevdoc_docname = qo.name
|
||||||
|
and qo.valid_till < so.transaction_date""" # check if SO was created after quotation expired
|
||||||
|
|
||||||
|
frappe.db.sql(
|
||||||
|
"""UPDATE `tabQuotation` qo SET qo.status = 'Expired' WHERE {cond} and exists({invalid_so_against_quo})"""
|
||||||
|
.format(cond=cond, invalid_so_against_quo=invalid_so_against_quo)
|
||||||
|
)
|
||||||
|
|
||||||
|
valid_so_against_quo = """
|
||||||
|
SELECT
|
||||||
|
so.name FROM `tabSales Order` so, `tabSales Order Item` so_item
|
||||||
|
WHERE
|
||||||
|
so_item.docstatus = 1 and so.docstatus = 1
|
||||||
|
and so_item.parent = so.name
|
||||||
|
and so_item.prevdoc_docname = qo.name
|
||||||
|
and qo.valid_till >= so.transaction_date""" # check if SO was created before quotation expired
|
||||||
|
|
||||||
|
frappe.db.sql(
|
||||||
|
"""UPDATE `tabQuotation` qo SET qo.status = 'Closed' WHERE {cond} and exists({valid_so_against_quo})"""
|
||||||
|
.format(cond=cond, valid_so_against_quo=valid_so_against_quo)
|
||||||
|
)
|
6
erpnext/patches/v12_0/set_default_payroll_based_on.py
Normal file
6
erpnext/patches/v12_0/set_default_payroll_based_on.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("hr", "doctype", "hr_settings")
|
||||||
|
frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave")
|
@ -0,0 +1,7 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
job = frappe.db.exists('Scheduled Job Type', 'patient_appointment.send_appointment_reminder')
|
||||||
|
if job:
|
||||||
|
method = 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder'
|
||||||
|
frappe.db.set_value('Scheduled Job Type', job, 'method', method)
|
0
erpnext/patches/v13_0/__init__.py
Normal file
0
erpnext/patches/v13_0/__init__.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
if not frappe.db.table_exists("Payroll Period"):
|
||||||
|
return
|
||||||
|
|
||||||
|
for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"):
|
||||||
|
frappe.reload_doc("hr", "doctype", doctype)
|
||||||
|
|
||||||
|
|
||||||
|
for company in frappe.get_all("Company"):
|
||||||
|
payroll_periods = frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
name, start_date, end_date, standard_tax_exemption_amount
|
||||||
|
FROM
|
||||||
|
`tabPayroll Period`
|
||||||
|
WHERE company=%s
|
||||||
|
ORDER BY start_date DESC
|
||||||
|
""", company.name, as_dict = 1)
|
||||||
|
|
||||||
|
for i, period in enumerate(payroll_periods):
|
||||||
|
income_tax_slab = frappe.new_doc("Income Tax Slab")
|
||||||
|
income_tax_slab.name = "Tax Slab:" + period.name
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
income_tax_slab.disabled = 0
|
||||||
|
else:
|
||||||
|
income_tax_slab.disabled = 1
|
||||||
|
|
||||||
|
income_tax_slab.effective_from = period.start_date
|
||||||
|
income_tax_slab.company = company.name
|
||||||
|
income_tax_slab.allow_tax_exemption = 1
|
||||||
|
income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount
|
||||||
|
|
||||||
|
income_tax_slab.flags.ignore_mandatory = True
|
||||||
|
income_tax_slab.submit()
|
||||||
|
|
||||||
|
frappe.db.sql(
|
||||||
|
""" UPDATE `tabTaxable Salary Slab`
|
||||||
|
SET parent = %s , parentfield = 'slabs' , parenttype = "Income Tax Slab"
|
||||||
|
WHERE parent = %s
|
||||||
|
""", (income_tax_slab.name, period.name), as_dict = 1)
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE
|
||||||
|
`tabSalary Structure Assignment`
|
||||||
|
set
|
||||||
|
income_tax_slab = %s
|
||||||
|
where
|
||||||
|
company = %s
|
||||||
|
and from_date >= %s
|
||||||
|
and docstatus < 2
|
||||||
|
""", (income_tax_slab.name, company.name, period.start_date))
|
||||||
|
|
||||||
|
# move other incomes to separate document
|
||||||
|
migrated = []
|
||||||
|
proofs = frappe.get_all("Employee Tax Exemption Proof Submission",
|
||||||
|
filters = {'docstatus': 1},
|
||||||
|
fields =['payroll_period', 'employee', 'company', 'income_from_other_sources']
|
||||||
|
)
|
||||||
|
for proof in proofs:
|
||||||
|
if proof.income_from_other_sources:
|
||||||
|
employee_other_income = frappe.new_doc("Employee Other Income")
|
||||||
|
employee_other_income.employee = proof.employee
|
||||||
|
employee_other_income.payroll_period = proof.payroll_period
|
||||||
|
employee_other_income.company = proof.company
|
||||||
|
employee_other_income.amount = proof.income_from_other_sources
|
||||||
|
|
||||||
|
try:
|
||||||
|
employee_other_income.submit()
|
||||||
|
migrated.append([proof.employee, proof.payroll_period])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
declerations = frappe.get_all("Employee Tax Exemption Declaration",
|
||||||
|
filters = {'docstatus': 1},
|
||||||
|
fields =['payroll_period', 'employee', 'company', 'income_from_other_sources']
|
||||||
|
)
|
||||||
|
|
||||||
|
for declaration in declerations:
|
||||||
|
if declaration.income_from_other_sources \
|
||||||
|
and [declaration.employee, declaration.payroll_period] not in migrated:
|
||||||
|
employee_other_income = frappe.new_doc("Employee Other Income")
|
||||||
|
employee_other_income.employee = declaration.employee
|
||||||
|
employee_other_income.payroll_period = declaration.payroll_period
|
||||||
|
employee_other_income.company = declaration.company
|
||||||
|
employee_other_income.amount = declaration.income_from_other_sources
|
||||||
|
|
||||||
|
try:
|
||||||
|
employee_other_income.submit()
|
||||||
|
except:
|
||||||
|
pass
|
@ -175,6 +175,20 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.frm.fields_dict["items"].grid.get_field('blanket_order')) {
|
||||||
|
this.frm.set_query("blanket_order", "items", function(doc, cdt, cdn) {
|
||||||
|
var item = locals[cdt][cdn];
|
||||||
|
return {
|
||||||
|
query: "erpnext.controllers.queries.get_blanket_orders",
|
||||||
|
filters: {
|
||||||
|
"company": doc.company,
|
||||||
|
"blanket_order_type": doc.doctype === "Sales Order" ? "Selling" : "Purchasing",
|
||||||
|
"item": item.item_code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
onload: function() {
|
onload: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
@ -288,7 +302,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
this.setup_sms();
|
this.setup_sms();
|
||||||
this.setup_quality_inspection();
|
this.setup_quality_inspection();
|
||||||
let scan_barcode_field = this.frm.get_field('scan_barcode');
|
let scan_barcode_field = this.frm.get_field('scan_barcode');
|
||||||
if (scan_barcode_field) {
|
if (scan_barcode_field && scan_barcode_field.get_value()) {
|
||||||
scan_barcode_field.set_value("");
|
scan_barcode_field.set_value("");
|
||||||
scan_barcode_field.set_new_description("");
|
scan_barcode_field.set_new_description("");
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ doctypes_with_dimensions.forEach((doctype) => {
|
|||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
frappe.model.with_doctype(dimension['document_type'], () => {
|
frappe.model.with_doctype(dimension['document_type'], () => {
|
||||||
if (frappe.meta.has_field(dimension['document_type'], 'is_group')) {
|
if(frappe.meta.has_field(dimension['document_type'], 'is_group')) {
|
||||||
frm.set_query(dimension['fieldname'], {
|
frm.set_query(dimension['fieldname'], {
|
||||||
"is_group": 0
|
"is_group": 0
|
||||||
});
|
});
|
||||||
@ -42,19 +42,21 @@ doctypes_with_dimensions.forEach((doctype) => {
|
|||||||
|
|
||||||
update_dimension: function(frm) {
|
update_dimension: function(frm) {
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
if (frm.is_new()) {
|
if(frm.is_new()) {
|
||||||
if (frm.doc.company && Object.keys(default_dimensions || {}).length > 0
|
if(frm.doc.company && Object.keys(default_dimensions || {}).length > 0
|
||||||
&& default_dimensions[frm.doc.company]) {
|
&& default_dimensions[frm.doc.company]) {
|
||||||
|
|
||||||
if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
|
let default_dimension = default_dimensions[frm.doc.company][dimension['document_type']];
|
||||||
frm.set_value(dimension['fieldname'],
|
|
||||||
default_dimensions[frm.doc.company][dimension['document_type']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$.each(frm.doc.items || frm.doc.accounts || [], function(i, row) {
|
if(default_dimension) {
|
||||||
frappe.model.set_value(row.doctype, row.name, dimension['fieldname'],
|
if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
|
||||||
default_dimensions[frm.doc.company][dimension['document_type']])
|
frm.set_value(dimension['fieldname'], default_dimension);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
$.each(frm.doc.items || frm.doc.accounts || [], function(i, row) {
|
||||||
|
frappe.model.set_value(row.doctype, row.name, dimension['fieldname'], default_dimension);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -71,20 +73,6 @@ child_docs.forEach((doctype) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
accounts_add: function(frm, cdt, cdn) {
|
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
|
||||||
var row = frappe.get_doc(cdt, cdn);
|
|
||||||
frm.script_manager.copy_from_first_row("accounts", row, [dimension['fieldname']]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
items_add: function(frm, cdt, cdn) {
|
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
|
||||||
var row = frappe.get_doc(cdt, cdn);
|
|
||||||
frm.script_manager.copy_from_first_row("items", row, [dimension['fieldname']]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
accounts_add: function(frm, cdt, cdn) {
|
accounts_add: function(frm, cdt, cdn) {
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
var row = frappe.get_doc(cdt, cdn);
|
var row = frappe.get_doc(cdt, cdn);
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Lower Deduction Certificate', {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "field:certificate_no",
|
||||||
|
"creation": "2020-03-10 23:12:10.072631",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"certificate_details_section",
|
||||||
|
"section_code",
|
||||||
|
"fiscal_year",
|
||||||
|
"column_break_3",
|
||||||
|
"certificate_no",
|
||||||
|
"section_break_3",
|
||||||
|
"supplier",
|
||||||
|
"column_break_7",
|
||||||
|
"pan_no",
|
||||||
|
"validity_details_section",
|
||||||
|
"valid_from",
|
||||||
|
"column_break_10",
|
||||||
|
"valid_upto",
|
||||||
|
"section_break_9",
|
||||||
|
"rate",
|
||||||
|
"column_break_14",
|
||||||
|
"certificate_limit"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "certificate_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Certificate No",
|
||||||
|
"reqd": 1,
|
||||||
|
"unique": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_code",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Section Code",
|
||||||
|
"options": "192\n193\n194\n194A\n194C\n194D\n194H\n194I\n194J\n194LA\n194LBB\n194LBC\n195",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_3",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Deductee Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Supplier",
|
||||||
|
"options": "Supplier",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "supplier.pan",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "pan_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "PAN No",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "validity_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Validity Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "valid_upto",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Valid Upto",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_9",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "rate",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"label": "Rate Of TDS As Per Certificate",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "certificate_limit",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Certificate Limit",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "certificate_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Certificate Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_10",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_14",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_7",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "valid_from",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Valid From",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fiscal_year",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Fiscal Year",
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-04-23 23:04:41.203721",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "Lower Deduction Certificate",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import getdate
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
|
class LowerDeductionCertificate(Document):
|
||||||
|
def validate(self):
|
||||||
|
if getdate(self.valid_upto) < getdate(self.valid_from):
|
||||||
|
frappe.throw(_("Valid Upto date cannot be before Valid From date"))
|
||||||
|
|
||||||
|
fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True)
|
||||||
|
|
||||||
|
if not (fiscal_year.year_start_date <= getdate(self.valid_from) \
|
||||||
|
<= fiscal_year.year_end_date):
|
||||||
|
frappe.throw(_("Valid From date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
|
||||||
|
|
||||||
|
if not (fiscal_year.year_start_date <= getdate(self.valid_upto) \
|
||||||
|
<= fiscal_year.year_end_date):
|
||||||
|
frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestLowerDeductionCertificate(unittest.TestCase):
|
||||||
|
pass
|
@ -61,7 +61,7 @@ def add_custom_roles_for_reports():
|
|||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
def add_permissions():
|
def add_permissions():
|
||||||
for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report'):
|
for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'):
|
||||||
add_permission(doctype, 'All', 0)
|
add_permission(doctype, 'All', 0)
|
||||||
for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
|
for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
|
||||||
add_permission(doctype, role, 0)
|
add_permission(doctype, role, 0)
|
||||||
@ -531,12 +531,18 @@ def make_fixtures(company=None):
|
|||||||
|
|
||||||
def set_salary_components(docs):
|
def set_salary_components(docs):
|
||||||
docs.extend([
|
docs.extend([
|
||||||
{'doctype': 'Salary Component', 'salary_component': 'Professional Tax', 'description': 'Professional Tax', 'type': 'Deduction'},
|
{'doctype': 'Salary Component', 'salary_component': 'Professional Tax',
|
||||||
{'doctype': 'Salary Component', 'salary_component': 'Provident Fund', 'description': 'Provident fund', 'type': 'Deduction'},
|
'description': 'Professional Tax', 'type': 'Deduction', 'exempted_from_income_tax': 1},
|
||||||
{'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', 'description': 'House Rent Allowance', 'type': 'Earning'},
|
{'doctype': 'Salary Component', 'salary_component': 'Provident Fund',
|
||||||
{'doctype': 'Salary Component', 'salary_component': 'Basic', 'description': 'Basic', 'type': 'Earning'},
|
'description': 'Provident fund', 'type': 'Deduction', 'is_tax_applicable': 1},
|
||||||
{'doctype': 'Salary Component', 'salary_component': 'Arrear', 'description': 'Arrear', 'type': 'Earning'},
|
{'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance',
|
||||||
{'doctype': 'Salary Component', 'salary_component': 'Leave Encashment', 'description': 'Leave Encashment', 'type': 'Earning'}
|
'description': 'House Rent Allowance', 'type': 'Earning', 'is_tax_applicable': 1},
|
||||||
|
{'doctype': 'Salary Component', 'salary_component': 'Basic',
|
||||||
|
'description': 'Basic', 'type': 'Earning', 'is_tax_applicable': 1},
|
||||||
|
{'doctype': 'Salary Component', 'salary_component': 'Arrear',
|
||||||
|
'description': 'Arrear', 'type': 'Earning', 'is_tax_applicable': 1},
|
||||||
|
{'doctype': 'Salary Component', 'salary_component': 'Leave Encashment',
|
||||||
|
'description': 'Leave Encashment', 'type': 'Earning', 'is_tax_applicable': 1}
|
||||||
])
|
])
|
||||||
|
|
||||||
def set_tax_withholding_category(company):
|
def set_tax_withholding_category(company):
|
||||||
|
@ -288,7 +288,7 @@ def calculate_annual_eligible_hra_exemption(doc):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
|
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
|
||||||
salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1)
|
salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1, ignore_permissions=True)
|
||||||
basic_amt, hra_amt = 0, 0
|
basic_amt, hra_amt = 0, 0
|
||||||
for earning in salary_slip.earnings:
|
for earning in salary_slip.earnings:
|
||||||
if earning.salary_component == basic_component:
|
if earning.salary_component == basic_component:
|
||||||
@ -372,7 +372,6 @@ def calculate_hra_exemption_for_period(doc):
|
|||||||
return exemptions
|
return exemptions
|
||||||
|
|
||||||
def get_ewb_data(dt, dn):
|
def get_ewb_data(dt, dn):
|
||||||
dn = dn.split(',')
|
|
||||||
|
|
||||||
ewaybills = []
|
ewaybills = []
|
||||||
for doc_name in dn:
|
for doc_name in dn:
|
||||||
@ -453,16 +452,22 @@ def get_ewb_data(dt, dn):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def generate_ewb_json(dt, dn):
|
def generate_ewb_json(dt, dn):
|
||||||
|
dn = json.loads(dn)
|
||||||
|
return get_ewb_data(dt, dn)
|
||||||
|
|
||||||
data = get_ewb_data(dt, dn)
|
@frappe.whitelist()
|
||||||
|
def download_ewb_json():
|
||||||
|
data = frappe._dict(frappe.local.form_dict)
|
||||||
|
|
||||||
frappe.local.response.filecontent = json.dumps(data, indent=4, sort_keys=True)
|
frappe.local.response.filecontent = json.dumps(data['data'], indent=4, sort_keys=True)
|
||||||
frappe.local.response.type = 'download'
|
frappe.local.response.type = 'download'
|
||||||
|
|
||||||
if len(data['billLists']) > 1:
|
billList = json.loads(data['data'])['billLists']
|
||||||
|
|
||||||
|
if len(billList) > 1:
|
||||||
doc_name = 'Bulk'
|
doc_name = 'Bulk'
|
||||||
else:
|
else:
|
||||||
doc_name = dn
|
doc_name = data['docname']
|
||||||
|
|
||||||
frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(doc_name, frappe.utils.random_string(5))
|
frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(doc_name, frappe.utils.random_string(5))
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Retail",
|
"label": "Retail",
|
||||||
"modified": "2020-04-01 11:28:50.966145",
|
"modified": "2020-04-26 22:42:39.346750",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Retail",
|
"name": "Retail",
|
||||||
|
@ -11,7 +11,8 @@ def get_data():
|
|||||||
'non_standard_fieldnames': {
|
'non_standard_fieldnames': {
|
||||||
'Payment Entry': 'party',
|
'Payment Entry': 'party',
|
||||||
'Quotation': 'party_name',
|
'Quotation': 'party_name',
|
||||||
'Opportunity': 'party_name'
|
'Opportunity': 'party_name',
|
||||||
|
'Bank Account': 'party'
|
||||||
},
|
},
|
||||||
'dynamic_links': {
|
'dynamic_links': {
|
||||||
'party_name': ['Customer', 'quotation_to']
|
'party_name': ['Customer', 'quotation_to']
|
||||||
@ -27,7 +28,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Payments'),
|
'label': _('Payments'),
|
||||||
'items': ['Payment Entry']
|
'items': ['Payment Entry', 'Bank Account']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Support'),
|
'label': _('Support'),
|
||||||
|
@ -193,12 +193,23 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
|||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
def set_expired_status():
|
def set_expired_status():
|
||||||
frappe.db.sql("""
|
# filter out submitted non expired quotations whose validity has been ended
|
||||||
UPDATE
|
cond = "qo.docstatus = 1 and qo.status != 'Expired' and qo.valid_till < %s"
|
||||||
`tabQuotation` SET `status` = 'Expired'
|
# check if those QUO have SO against it
|
||||||
WHERE
|
so_against_quo = """
|
||||||
`status` not in ('Ordered', 'Expired', 'Lost', 'Cancelled') AND `valid_till` < %s
|
SELECT
|
||||||
""", (nowdate()))
|
so.name FROM `tabSales Order` so, `tabSales Order Item` so_item
|
||||||
|
WHERE
|
||||||
|
so_item.docstatus = 1 and so.docstatus = 1
|
||||||
|
and so_item.parent = so.name
|
||||||
|
and so_item.prevdoc_docname = qo.name"""
|
||||||
|
|
||||||
|
# if not exists any SO, set status as Expired
|
||||||
|
frappe.db.sql(
|
||||||
|
"""UPDATE `tabQuotation` qo SET qo.status = 'Expired' WHERE {cond} and not exists({so_against_quo})"""
|
||||||
|
.format(cond=cond, so_against_quo=so_against_quo),
|
||||||
|
(nowdate())
|
||||||
|
)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_invoice(source_name, target_doc=None):
|
def make_sales_invoice(source_name, target_doc=None):
|
||||||
|
@ -65,15 +65,6 @@ frappe.ui.form.on("Sales Order", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("blanket_order", "items", function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"docstatus": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
erpnext.queries.setup_warehouse_query(frm);
|
erpnext.queries.setup_warehouse_query(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -148,7 +139,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
|
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
|
||||||
|
|
||||||
const order_is_a_sale = ["Sales", "Shopping Cart"].indexOf(doc.order_type) !== -1;
|
const order_is_a_sale = ["Sales", "Shopping Cart"].indexOf(doc.order_type) !== -1;
|
||||||
const order_is_maintenance = ["Maintenance"].indexOf(doc.order_type) !== -1;
|
const order_is_maintenance = ["Maintenance"].indexOf(doc.order_type) !== -1;
|
||||||
// order type has been customised then show all the action buttons
|
// order type has been customised then show all the action buttons
|
||||||
|
@ -3,21 +3,28 @@
|
|||||||
erpnext.setup_auto_gst_taxation('Delivery Note');
|
erpnext.setup_auto_gst_taxation('Delivery Note');
|
||||||
|
|
||||||
frappe.ui.form.on('Delivery Note', {
|
frappe.ui.form.on('Delivery Note', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(frm.doc.docstatus == 1 && !frm.is_dirty() && !frm.doc.ewaybill) {
|
if(frm.doc.docstatus == 1 && !frm.is_dirty() && !frm.doc.ewaybill) {
|
||||||
frm.add_custom_button('E-Way Bill JSON', () => {
|
frm.add_custom_button('E-Way Bill JSON', () => {
|
||||||
var w = window.open(
|
frappe.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
'dt': frm.doc.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
'dn': [frm.doc.name]
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
|
data: r.message,
|
||||||
|
docname: frm.doc.name
|
||||||
|
};
|
||||||
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}, __("Create"));
|
}, __("Create"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user