Merge branch 'develop' into patch-8

This commit is contained in:
Deepesh Garg 2020-04-30 11:37:27 +05:30 committed by GitHub
commit 758f0f0780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
182 changed files with 3749 additions and 2908 deletions

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import nowdate
from frappe.utils import nowdate, now_datetime
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
@ -13,27 +13,28 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
budget.cancel()
jv.cancel()
def test_monthly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28")
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@ -41,14 +42,14 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_exception_approver_role(self):
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-03-02")
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@ -112,16 +113,17 @@ class TestBudget(unittest.TestCase):
budget.load_from_db()
budget.cancel()
po.cancel()
def test_monthly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "project")
set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-02-28")
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@ -129,86 +131,76 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_yearly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC", posting_date="2013-03-28")
"_Test Bank - _TC", 250000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
budget.cancel()
def test_yearly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "project")
set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-03-28")
"_Test Bank - _TC", 250000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
budget.cancel()
def test_monthly_budget_on_cancellation1(self):
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
for i in range(now_datetime().month):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
self.assertRaises(BudgetError, jv1.cancel)
self.assertRaises(BudgetError, jv.cancel)
budget.load_from_db()
budget.cancel()
def test_monthly_budget_on_cancellation2(self):
set_total_expense_zero("2013-02-28", "project")
set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project")
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project")
for i in range(now_datetime().month):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project")
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project")
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
self.assertRaises(BudgetError, jv1.cancel)
self.assertRaises(BudgetError, jv.cancel)
budget.load_from_db()
budget.cancel()
def test_monthly_budget_against_group_cost_center(self):
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
set_total_expense_zero(nowdate(), "cost_center")
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date="2013-02-28")
"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@ -231,7 +223,7 @@ class TestBudget(unittest.TestCase):
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, cost_center, posting_date="2013-02-28")
"_Test Bank - _TC", 40000, cost_center, posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@ -246,12 +238,14 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
else:
budget_against = budget_against_CC or "_Test Cost Center - _TC"
fiscal_year = get_fiscal_year(nowdate())[0]
args = frappe._dict({
"account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date,
"company": "_Test Company",
"fiscal_year": "_Test Fiscal Year 2013",
"fiscal_year": fiscal_year,
"budget_against_field": budget_against_field,
})
@ -263,10 +257,10 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
if existing_expense:
if budget_against_field == "cost_center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date=nowdate())
def make_budget(**args):
args = frappe._dict(args)
@ -274,10 +268,13 @@ def make_budget(**args):
budget_against=args.budget_against
cost_center=args.cost_center
fiscal_year = get_fiscal_year(nowdate())[0]
if budget_against == "Project":
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Project/_Test Fiscal Year 2013%")})
project_name = "{0}%".format("_Test Project/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", project_name)})
else:
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/_Test Fiscal Year 2013")
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)})
for d in budget_list:
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
@ -290,8 +287,10 @@ def make_budget(**args):
else:
budget.cost_center =cost_center or "_Test Cost Center - _TC"
monthly_distribution = frappe.get_doc("Monthly Distribution", "_Test Distribution")
monthly_distribution.fiscal_year = fiscal_year
budget.fiscal_year = "_Test Fiscal Year 2013"
budget.fiscal_year = fiscal_year
budget.monthly_distribution = "_Test Distribution"
budget.company = "_Test Company"
budget.applicable_on_booking_actual_expenses = 1
@ -300,7 +299,7 @@ def make_budget(**args):
budget.budget_against = budget_against
budget.append("accounts", {
"account": "_Test Account Cost for Goods Sold - _TC",
"budget_amount": 100000
"budget_amount": 200000
})
if args.applicable_on_material_request:

View File

@ -18,7 +18,7 @@ frappe.ui.form.on('Cost Center', {
},
refresh: function(frm) {
if (!frm.is_new()) {
frm.add_custom_button(__('Update Cost Center Number'), function () {
frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
frm.trigger("update_cost_center_number");
});
}
@ -47,35 +47,45 @@ frappe.ui.form.on('Cost Center', {
},
update_cost_center_number: function(frm) {
var d = new frappe.ui.Dialog({
title: __('Update Cost Center Number'),
title: __('Update Cost Center Name / Number'),
fields: [
{
"label": 'Cost Center Number',
"label": "Cost Center Name",
"fieldname": "cost_center_name",
"fieldtype": "Data",
"reqd": 1,
"default": frm.doc.cost_center_name
},
{
"label": "Cost Center Number",
"fieldname": "cost_center_number",
"fieldtype": "Data",
"reqd": 1
"reqd": 1,
"default": frm.doc.cost_center_number
}
],
primary_action: function() {
var data = d.get_values();
if(data.cost_center_number === frm.doc.cost_center_number) {
if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) {
d.hide();
return;
}
frappe.dom.freeze();
frappe.call({
method: "erpnext.accounts.utils.update_number_field",
method: "erpnext.accounts.utils.update_cost_center",
args: {
doctype_name: frm.doc.doctype,
name: frm.doc.name,
field_name: d.fields[0].fieldname,
number_value: data.cost_center_number,
docname: frm.doc.name,
cost_center_name: data.cost_center_name,
cost_center_number: data.cost_center_number,
company: frm.doc.company
},
callback: function(r) {
frappe.dom.unfreeze();
if(!r.exc) {
if(r.message) {
frappe.set_route("Form", "Cost Center", r.message);
} else {
me.frm.set_value("cost_center_name", data.cost_center_name);
me.frm.set_value("cost_center_number", data.cost_center_number);
}
d.hide();

View File

@ -2,7 +2,6 @@
"actions": [],
"allow_copy": 1,
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-01-23 19:57:17",
"description": "Track separate Income and Expense for product verticals or divisions.",
"doctype": "DocType",
@ -126,7 +125,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2020-03-18 17:59:04.321637",
"modified": "2020-04-29 16:09:30.025214",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cost Center",

View File

@ -30,7 +30,8 @@
"company",
"finance_book",
"to_rename",
"due_date"
"due_date",
"is_cancelled"
],
"fields": [
{
@ -245,12 +246,18 @@
"fieldname": "due_date",
"fieldtype": "Date",
"label": "Due Date"
},
{
"default": "0",
"fieldname": "is_cancelled",
"fieldtype": "Check",
"label": "Is Cancelled"
}
],
"icon": "fa fa-list",
"idx": 1,
"in_create": 1,
"modified": "2020-03-28 16:22:33.766994",
"modified": "2020-04-07 16:22:33.766994",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

View File

@ -30,23 +30,20 @@ class GLEntry(Document):
self.pl_must_have_cost_center()
self.validate_cost_center()
if not self.flags.from_repost:
self.check_pl_account()
self.validate_party()
self.validate_currency()
self.check_pl_account()
self.validate_party()
self.validate_currency()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
if not from_repost:
self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs()
check_freezing_date(self.posting_date, adv_adj)
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs()
validate_frozen_account(self.account, adv_adj)
validate_balance_type(self.account, adv_adj)
# Update outstanding amt on against voucher
if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
and self.against_voucher and update_outstanding == 'Yes':
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher)
@ -159,7 +156,6 @@ class GLEntry(Document):
if self.party_type and self.party:
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
def validate_and_set_fiscal_year(self):
if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
@ -176,19 +172,6 @@ def validate_balance_type(account, adv_adj=False):
(balance_must_be=="Credit" and flt(balance) > 0):
frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be)))
def check_freezing_date(posting_date, adv_adj=False):
"""
Nobody can do GL Entries where posting date is before freezing date
except authorized person
"""
if not adv_adj:
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
if acc_frozen_upto:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if getdate(posting_date) <= getdate(acc_frozen_upto) \
and not frozen_accounts_modifier in frappe.get_roles():
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
if party_type and party:
party_condition = " and party_type={0} and party={1}"\

View File

@ -57,6 +57,7 @@ class JournalEntry(AccountsController):
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.make_gl_entries(1)
self.update_advance_paid()
self.update_expense_claim()
@ -594,7 +595,7 @@ class JournalEntry(AccountsController):
for d in self.accounts:
if d.reference_type=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
update_reimbursed_amount(doc)
update_reimbursed_amount(doc, jv=self.name)
def validate_expense_claim(self):

View File

@ -75,6 +75,7 @@ class PaymentEntry(AccountsController):
self.set_status()
def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.setup_party_account_field()
self.make_gl_entries(cancel=1)
self.update_outstanding_amounts()
@ -571,7 +572,7 @@ class PaymentEntry(AccountsController):
for d in self.get("references"):
if d.reference_doctype=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
update_reimbursed_amount(doc)
update_reimbursed_amount(doc, self.name)
def on_recurring(self, reference_doc, auto_repeat_doc):
self.reference_no = reference_doc.name

View File

@ -35,8 +35,6 @@ class TestPaymentEntry(unittest.TestCase):
pe.cancel()
self.assertFalse(self.get_gle(pe.name))
so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
self.assertEqual(so_advance_paid, 0)
@ -124,7 +122,6 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(outstanding_amount, 0)
pe.cancel()
self.assertFalse(self.get_gle(pe.name))
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 100)
@ -381,7 +378,6 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(outstanding_amount, 0)
pe3.cancel()
self.assertFalse(self.get_gle(pe3.name))
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, -100)

View File

@ -326,7 +326,7 @@ def make_payment_request(**args):
"reference_doctype": args.dt,
"reference_name": args.dn,
"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
})
@ -420,7 +420,7 @@ def make_payment_entry(docname):
def update_payment_req_status(doc, method):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
for ref in doc.references:
payment_request_name = frappe.db.get_value("Payment Request",
{"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)
pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
status = pay_req_doc.status
if status != "Paid" and not ref_details.outstanding_amount:
status = 'Paid'
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:

View File

@ -5,7 +5,7 @@ frappe.ui.form.on('Period Closing Voucher', {
onload: function(frm) {
if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date());
},
setup: function(frm) {
frm.set_query("closing_account_head", function() {
return {
@ -18,9 +18,9 @@ frappe.ui.form.on('Period Closing Voucher', {
}
});
},
refresh: function(frm) {
if(frm.doc.docstatus==1) {
if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
@ -33,5 +33,5 @@ frappe.ui.form.on('Period Closing Voucher', {
}, "fa fa-table");
}
}
})

View File

@ -17,8 +17,9 @@ class PeriodClosingVoucher(AccountsController):
self.make_gl_entries()
def on_cancel(self):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
from erpnext.accounts.general_ledger import make_reverse_gl_entries
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name, cancel=True)
def validate_account_head(self):
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")

View File

@ -261,12 +261,25 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
price_list: this.frm.doc.buying_price_list
}, function() {
me.apply_pricing_rule();
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("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() {
var me = this;
if(this.frm.doc.credit_to) {
@ -369,11 +382,6 @@ function hide_fields(doc) {
cur_frm.refresh_fields();
}
cur_frm.cscript.update_stock = function(doc, dt, dn) {
hide_fields(doc, dt, dn);
this.frm.fields_dict.items.grid.toggle_reqd("item_code", doc.update_stock? true: false)
}
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
return {
filters: [
@ -515,5 +523,10 @@ frappe.ui.form.on("Purchase Invoice", {
erpnext.buying.get_default_bom(frm);
}
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
},
update_stock: function(frm) {
hide_fields(frm.doc);
frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock? true: false);
}
})

View File

@ -13,6 +13,7 @@
"supplier_name",
"tax_id",
"due_date",
"tax_withholding_category",
"column_break1",
"company",
"posting_date",
@ -1294,13 +1295,21 @@
"fieldtype": "Check",
"label": "Is Internal Supplier",
"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",
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2020-04-17 13:05:25.199832",
"modified": "2020-04-18 13:05:25.199832",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@ -14,7 +14,7 @@ from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, make_reverse_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
@ -382,7 +382,7 @@ class PurchaseInvoice(BuyingController):
self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
def make_gl_entries(self, gl_entries=None):
if not self.grand_total:
return
if not gl_entries:
@ -391,21 +391,17 @@ class PurchaseInvoice(BuyingController):
if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
if self.docstatus == 1:
make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False)
elif self.docstatus == 2:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) and self.auto_accounting_for_stock:
from erpnext.controllers.stock_controller import update_gl_entries_after
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@ -934,6 +930,7 @@ class PurchaseInvoice(BuyingController):
frappe.db.set(self, 'status', 'Cancelled')
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
def update_project(self):
project_list = []
@ -1002,7 +999,7 @@ class PurchaseInvoice(BuyingController):
if not self.apply_tds:
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:
return

View File

@ -138,13 +138,12 @@
"row_id": 7
}
],
"posting_date": "2013-02-03",
"supplier": "_Test Supplier",
"supplier_name": "_Test Supplier"
},
{
"bill_no": "NA",
"buying_price_list": "_Test Price List",
@ -204,7 +203,6 @@
"tax_amount": 150.0
}
],
"posting_date": "2013-02-03",
"supplier": "_Test Supplier",
"supplier_name": "_Test Supplier"
}

View File

@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "hash",
"creation": "2013-05-21 16:16:04",
"doctype": "DocType",
@ -14,11 +15,11 @@
"col_break1",
"account_head",
"description",
"section_break_10",
"rate",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"section_break_10",
"rate",
"section_break_9",
"tax_amount",
"tax_amount_after_discount_amount",
@ -27,8 +28,7 @@
"base_tax_amount",
"base_total",
"base_tax_amount_after_discount_amount",
"item_wise_tax_detail",
"parenttype"
"item_wise_tax_detail"
],
"fields": [
{
@ -53,6 +53,7 @@
},
{
"columns": 2,
"default": "On Net Total",
"fieldname": "charge_type",
"fieldtype": "Select",
"in_list_view": 1,
@ -196,15 +197,6 @@
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "parenttype",
"fieldtype": "Data",
"hidden": 1,
"label": "Parenttype",
"oldfieldname": "parenttype",
"oldfieldtype": "Data",
"print_hide": 1
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
@ -217,11 +209,14 @@
],
"idx": 1,
"istable": 1,
"modified": "2019-05-25 23:08:38.281025",
"links": [],
"modified": "2020-03-12 14:53:47.679439",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -26,16 +26,24 @@ frappe.ui.form.on("Sales Invoice", {
&& !frm.doc.is_return && !frm.doc.ewaybill) {
frm.add_custom_button('E-Way Bill JSON', () => {
var w = window.open(
frappe.urllib.get_full_url(
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
+ "dt=" + encodeURIComponent(frm.doc.doctype)
+ "&dn=" + encodeURIComponent(frm.doc.name)
)
);
if (!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
frappe.call({
method: 'erpnext.regional.india.utils.generate_ewb_json',
args: {
'dt': frm.doc.doctype,
'dn': [frm.doc.name]
},
callback: function(r) {
if (r.message) {
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"));
}
}

View File

@ -16,17 +16,23 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
}
}
var w = window.open(
frappe.urllib.get_full_url(
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
+ "dt=" + encodeURIComponent(doclist.doctype)
+ "&dn=" + encodeURIComponent(docnames)
)
);
if (!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
frappe.call({
method: 'erpnext.regional.india.utils.generate_ewb_json',
args: {
'dt': doclist.doctype,
'dn': docnames
},
callback: function(r) {
if (r.message) {
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);

View File

@ -345,7 +345,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
set_dynamic_labels: function() {
this._super();
this.hide_fields(this.frm.doc);
this.frm.events.hide_fields(this.frm)
},
items_on_form_rendered: function() {
@ -404,7 +404,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
if(r.message && r.message.print_format) {
me.frm.pos_print_format = r.message.print_format;
}
me.frm.script_manager.trigger("update_stock");
me.frm.trigger("update_stock");
if(me.frm.doc.taxes_and_charges) {
me.frm.script_manager.trigger("taxes_and_charges");
}
@ -446,35 +446,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
// Hide Fields
// ------------
cur_frm.cscript.hide_fields = function(doc) {
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
hide_field(parent_fields);
} else {
for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]);
}
}
// India related fields
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
else hide_field(['c_form_applicable', 'c_form_no']);
this.frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
cur_frm.refresh_fields();
}
cur_frm.cscript.update_stock = function(doc, dt, dn) {
cur_frm.cscript.hide_fields(doc, dt, dn);
this.frm.fields_dict.items.grid.toggle_reqd("item_code", doc.update_stock? true: false)
}
cur_frm.cscript['Make Delivery Note'] = function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_delivery_note",
@ -587,7 +558,9 @@ frappe.ui.form.on('Sales Invoice', {
frm.set_query("account_for_change_amount", function() {
return {
filters: {
account_type: ['in', ["Cash", "Bank"]]
account_type: ['in', ["Cash", "Bank"]],
company: frm.doc.company,
is_group: 0
}
};
});
@ -668,7 +641,8 @@ frappe.ui.form.on('Sales Invoice', {
frm.fields_dict["loyalty_redemption_account"].get_query = function() {
return {
filters:{
"company": frm.doc.company
"company": frm.doc.company,
"is_group": 0
}
}
};
@ -677,7 +651,8 @@ frappe.ui.form.on('Sales Invoice', {
frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() {
return {
filters:{
"company": frm.doc.company
"company": frm.doc.company,
"is_group": 0
}
}
};
@ -715,6 +690,12 @@ frappe.ui.form.on('Sales Invoice', {
frm.redemption_conversion_factor = null;
},
update_stock: function(frm, dt, dn) {
frm.events.hide_fields(frm);
frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock);
frm.trigger('reset_posting_time');
},
redeem_loyalty_points: function(frm) {
frm.events.get_loyalty_details(frm);
},
@ -738,6 +719,29 @@ frappe.ui.form.on('Sales Invoice', {
}
},
hide_fields: function(frm) {
let doc = frm.doc;
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
hide_field(parent_fields);
} else {
for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]);
}
}
// India related fields
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
else hide_field(['c_form_applicable', 'c_form_no']);
frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
frm.refresh_fields();
},
get_loyalty_details: function(frm) {
if (frm.doc.customer && frm.doc.redeem_loyalty_points) {
frappe.call({

View File

@ -149,9 +149,9 @@
"edit_printing_settings",
"letter_head",
"group_same_items",
"language",
"column_break_84",
"select_print_heading",
"column_break_84",
"language",
"more_information",
"inter_company_invoice_reference",
"is_internal_customer",
@ -1579,7 +1579,7 @@
"idx": 181,
"is_submittable": 1,
"links": [],
"modified": "2020-04-17 12:38:41.435728",
"modified": "2020-04-29 13:37:09.355300",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@ -7,7 +7,6 @@ import frappe.defaults
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.controllers.stock_controller import update_gl_entries_after
from frappe.model.mapper import get_mapped_doc
from erpnext.accounts.doctype.sales_invoice.pos import update_multi_mode_option
@ -282,6 +281,8 @@ class SalesInvoice(SellingController):
if "Healthcare" in active_domains:
manage_invoice_submit_cancel(self, "on_cancel")
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.append({
@ -717,7 +718,9 @@ class SalesInvoice(SellingController):
if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1:
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
def make_gl_entries(self, gl_entries=None):
from erpnext.accounts.general_ledger import make_reverse_gl_entries
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not gl_entries:
gl_entries = self.get_gl_entries()
@ -729,23 +732,19 @@ class SalesInvoice(SellingController):
update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or
cint(self.redeem_loyalty_points)) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
if self.docstatus == 1:
make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False)
elif self.docstatus == 2:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
update_outstanding_amt(self.debit_to, "Customer", self.customer,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) \
and cint(auto_accounting_for_stock):
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) \
and cint(auto_accounting_for_stock):
from erpnext.accounts.general_ledger import delete_gl_entries
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries

View File

@ -364,7 +364,7 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
self.assertTrue(gle)
def test_tax_calculation_with_multiple_items(self):
si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True)
@ -678,14 +678,15 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
self.assertTrue(gle)
def test_pos_gl_entry_with_perpetual_inventory(self):
make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1
pos.update_stock = 1
@ -766,9 +767,13 @@ class TestSalesInvoice(unittest.TestCase):
def test_pos_change_amount(self):
make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1
pos.update_stock = 1
@ -787,8 +792,15 @@ class TestSalesInvoice(unittest.TestCase):
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
pos_profile = make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
item_code= "_Test FG Item",
warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1
pos.update_stock = 1
@ -891,11 +903,9 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
self.assertTrue(gle)
frappe.db.sql("delete from `tabPOS Profile`")
si.delete()
def test_pos_si_without_payment(self):
set_perpetual_inventory()
@ -1012,9 +1022,6 @@ class TestSalesInvoice(unittest.TestCase):
si.cancel()
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_name=%s""", si.name))
def test_serialized(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@ -1230,7 +1237,7 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
self.assertTrue(gle)
def test_invalid_currency(self):
# Customer currency = USD
@ -1892,7 +1899,7 @@ class TestSalesInvoice(unittest.TestCase):
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['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')

View File

@ -6,23 +6,42 @@ from __future__ import unicode_literals
import frappe
from frappe import _
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
class TaxWithholdingCategory(Document):
pass
def get_party_tax_withholding_details(ref_doc):
tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
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:
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)
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
if not tax_details:
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
.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)
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."))
def get_tax_row(tax_details, tds_amount):
return {
"category": "Total",
"add_deduct_tax": "Deduct",
@ -60,25 +80,36 @@ def get_tax_row(tax_details, 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
tds_amount = 0
tds_deducted = 0
def _get_tds(amount):
def _get_tds(amount, rate):
if amount <= 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("""
select voucher_no, credit
from `tabGL Entry`
where party=%s and fiscal_year=%s and credit > 0
""", (ref_doc.supplier, fiscal_year), as_dict=1)
where company = %s and
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]
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
@ -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
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:
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
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)'],
filters = {
'parent': ('in', vouchers), 'docstatus': 1,
'party': ref_doc.supplier,
'party': ('in', suppliers),
'reference_type': ('not in', ['Purchase Invoice'])
}, as_list=1)
supplier_credit_amount += (jv_supplier_credit_amt[0][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
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)):
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
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
if company:
condition += "and company =%s" % (company)
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("""
select distinct voucher_no
from `tabGL Entry`
where party=%s and %s and debit > 0
""", (supplier, condition)) or []
where party in %s and %s and debit > 0
""", (tuple(suppliers), condition)) or []
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
condition = ""
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
condition = "and 1=1"
if company:
condition = " and company=%s " % company
if len(suppliers) == 1:
suppliers.append(suppliers[0])
return flt(frappe.db.sql("""
select abs(sum(net_total))
from `tabPurchase Invoice`
where supplier=%s %s and is_return=1 and docstatus=1
and posting_date between %s and %s
""", (supplier, condition, year_start_date, year_end_date)))
where supplier in %s and is_return=1 and docstatus=1
and posting_date between %s and %s %s
""", (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

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe.utils import flt, cstr, cint, comma_and
from frappe.utils import flt, cstr, cint, comma_and, today, getdate, formatdate, now
from frappe import _
from erpnext.accounts.utils import get_stock_and_account_balance
from frappe.model.meta import get_field_precision
@ -15,17 +15,17 @@ class ClosedAccountingPeriod(frappe.ValidationError): pass
class StockAccountInvalidTransaction(frappe.ValidationError): pass
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes'):
if gl_map:
if not cancel:
validate_accounting_period(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
if gl_map and len(gl_map) > 1:
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
save_entries(gl_map, adv_adj, update_outstanding)
else:
frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."))
else:
delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
def validate_accounting_period(gl_map):
accounting_periods = frappe.db.sql(""" SELECT
@ -119,33 +119,36 @@ def check_if_in_list(gle, gl_map, dimensions=None):
if same_head:
return e
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost:
validate_cwip_accounts(gl_map)
def save_entries(gl_map, adv_adj, update_outstanding):
validate_cwip_accounts(gl_map)
round_off_debit_credit(gl_map)
if gl_map:
check_freezing_date(gl_map[0]["posting_date"], adv_adj)
for entry in gl_map:
make_entry(entry, adv_adj, update_outstanding, from_repost)
make_entry(entry, adv_adj, update_outstanding)
# check against budget
if not from_repost:
validate_expense_against_budget(entry)
validate_expense_against_budget(entry)
if not from_repost:
validate_account_for_perpetual_inventory(gl_map)
validate_account_for_perpetual_inventory(gl_map)
def make_entry(args, adv_adj, update_outstanding, from_repost=False):
def make_entry(args, adv_adj, update_outstanding):
gle = frappe.new_doc("GL Entry")
gle.update(args)
gle.flags.ignore_permissions = 1
gle.flags.from_repost = from_repost
gle.validate()
gle.db_insert()
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
gle.run_method("on_update_with_args", adv_adj, update_outstanding)
gle.flags.ignore_validate = True
gle.submit()
# check against budget
validate_expense_against_budget(args)
def validate_account_for_perpetual_inventory(gl_map):
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)):
account_list = [gl_entries.account for gl_entries in gl_map]
@ -169,33 +172,33 @@ def validate_account_for_perpetual_inventory(gl_map):
.format(account), StockAccountInvalidTransaction)
# This has been comment for a temporary, will add this code again on release of immutable ledger
# elif account_bal != stock_bal:
# precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
# currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
elif account_bal != stock_bal:
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
# diff = flt(stock_bal - account_bal, precision)
# error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
# stock_bal, account_bal, frappe.bold(account))
# error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
# stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
diff = flt(stock_bal - account_bal, precision)
error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
stock_bal, account_bal, frappe.bold(account))
error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
# db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
# db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
# journal_entry_args = {
# 'accounts':[
# {'account': account, db_or_cr_warehouse_account : abs(diff)},
# {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
# }
journal_entry_args = {
'accounts':[
{'account': account, db_or_cr_warehouse_account : abs(diff)},
{'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
}
# frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
# raise_exception=StockValueAndAccountBalanceOutOfSync,
# title=_('Values Out Of Sync'),
# primary_action={
# 'label': _('Make Journal Entry'),
# 'client_action': 'erpnext.route_to_adjustment_jv',
# 'args': journal_entry_args
# })
frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
raise_exception=StockValueAndAccountBalanceOutOfSync,
title=_('Values Out Of Sync'),
primary_action={
'label': _('Make Journal Entry'),
'client_action': 'erpnext.route_to_adjustment_jv',
'args': journal_entry_args
})
def validate_cwip_accounts(gl_map):
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
@ -282,31 +285,64 @@ def get_round_off_account_and_cost_center(company):
return round_off_account, round_off_cost_center
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"):
from erpnext.accounts.doctype.gl_entry.gl_entry import validate_balance_type, \
check_freezing_date, update_outstanding_amt, validate_frozen_account
def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"):
"""
Get original gl entries of the voucher
and make reverse gl entries by swapping debit and credit
"""
if not gl_entries:
gl_entries = frappe.db.sql("""
select account, posting_date, party_type, party, cost_center, fiscal_year,voucher_type,
voucher_no, against_voucher_type, against_voucher, cost_center, company
from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
gl_entries = frappe.get_all("GL Entry",
fields = ["*"],
filters = {
"voucher_type": voucher_type,
"voucher_no": voucher_no
})
if gl_entries:
set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
(voucher_type or gl_entries[0]["voucher_type"], voucher_no or gl_entries[0]["voucher_no"]))
for entry in gl_entries:
entry['name'] = None
debit = entry.get('debit', 0)
credit = entry.get('credit', 0)
for entry in gl_entries:
validate_frozen_account(entry["account"], adv_adj)
validate_balance_type(entry["account"], adv_adj)
if not adv_adj:
validate_expense_against_budget(entry)
debit_in_account_currency = entry.get('debit_in_account_currency', 0)
credit_in_account_currency = entry.get('credit_in_account_currency', 0)
if entry.get("against_voucher") and update_outstanding == 'Yes' and not adv_adj:
update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
entry.get("against_voucher"), on_cancel=True)
entry['debit'] = credit
entry['credit'] = debit
entry['debit_in_account_currency'] = credit_in_account_currency
entry['credit_in_account_currency'] = debit_in_account_currency
entry['remarks'] = "On cancellation of " + entry['voucher_no']
entry['is_cancelled'] = 1
entry['posting_date'] = today()
if entry['debit'] or entry['credit']:
make_entry(entry, adv_adj, "Yes")
def check_freezing_date(posting_date, adv_adj=False):
"""
Nobody can do GL Entries where posting date is before freezing date
except authorized person
"""
if not adv_adj:
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
if acc_frozen_upto:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if getdate(posting_date) <= getdate(acc_frozen_upto) \
and not frozen_accounts_modifier in frappe.get_roles():
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def set_as_cancel(voucher_type, voucher_no):
"""
Set is_cancelled=1 in all original gl entries for the voucher
"""
frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
modified=%s, modified_by=%s
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
(now(), frappe.session.user, voucher_type, voucher_no))

View File

@ -84,6 +84,7 @@ def get_balance_sheet_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)
company_currency = get_company_currency(filters)
data = []
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)
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

View File

@ -154,8 +154,12 @@ frappe.query_reports["General Ledger"] = {
{
"fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"),
"fieldtype": "Check",
"default": 1
"fieldtype": "Check"
},
{
"fieldname": "show_cancelled_entries",
"label": __("Show Cancelled Entries"),
"fieldtype": "Check"
}
]
}

View File

@ -188,6 +188,9 @@ def get_conditions(filters):
else:
conditions.append("finance_book in (%(finance_book)s)")
if not filters.get("show_cancelled_entries"):
conditions.append("is_cancelled = 0")
from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("GL Entry")

View File

@ -35,6 +35,12 @@ def execute(filters=None):
})
return columns, data
# to avoid error eg: gross_income[0] : list index out of range
if not gross_income:
gross_income = [{}]
if not gross_expense:
gross_expense = [{}]
data.append({
"account_name": "'" + _("Included in Gross Profit") + "'",
"account": "'" + _("Included in Gross Profit") + "'"

View File

@ -55,27 +55,27 @@ def get_columns(group_wise_columns, filters):
columns = []
column_map = frappe._dict({
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
"posting_date": _("Posting Date") + ":Date",
"posting_time": _("Posting Time"),
"item_code": _("Item Code") + ":Link/Item",
"item_name": _("Item Name"),
"item_group": _("Item Group") + ":Link/Item Group",
"brand": _("Brand"),
"description": _("Description"),
"warehouse": _("Warehouse") + ":Link/Warehouse",
"qty": _("Qty") + ":Float",
"base_rate": _("Avg. Selling Rate") + ":Currency/currency",
"buying_rate": _("Valuation Rate") + ":Currency/currency",
"base_amount": _("Selling Amount") + ":Currency/currency",
"buying_amount": _("Buying Amount") + ":Currency/currency",
"gross_profit": _("Gross Profit") + ":Currency/currency",
"gross_profit_percent": _("Gross Profit %") + ":Percent",
"project": _("Project") + ":Link/Project",
"posting_date": _("Posting Date") + ":Date:100",
"posting_time": _("Posting Time") + ":Data:100",
"item_code": _("Item Code") + ":Link/Item:100",
"item_name": _("Item Name") + ":Data:100",
"item_group": _("Item Group") + ":Link/Item Group:100",
"brand": _("Brand") + ":Link/Brand:100",
"description": _("Description") +":Data:100",
"warehouse": _("Warehouse") + ":Link/Warehouse:100",
"qty": _("Qty") + ":Float:80",
"base_rate": _("Avg. Selling Rate") + ":Currency/currency:100",
"buying_rate": _("Valuation Rate") + ":Currency/currency:100",
"base_amount": _("Selling Amount") + ":Currency/currency:100",
"buying_amount": _("Buying Amount") + ":Currency/currency:100",
"gross_profit": _("Gross Profit") + ":Currency/currency:100",
"gross_profit_percent": _("Gross Profit %") + ":Percent:100",
"project": _("Project") + ":Link/Project:100",
"sales_person": _("Sales person"),
"allocated_amount": _("Allocated Amount") + ":Currency/currency",
"customer": _("Customer") + ":Link/Customer",
"customer_group": _("Customer Group") + ":Link/Customer Group",
"territory": _("Territory") + ":Link/Territory"
"allocated_amount": _("Allocated Amount") + ":Currency/currency:100",
"customer": _("Customer") + ":Link/Customer:100",
"customer_group": _("Customer Group") + ":Link/Customer Group:100",
"territory": _("Territory") + ":Link/Territory:100"
})
for col in group_wise_columns.get(scrub(filters.group_by)):
@ -85,7 +85,8 @@ def get_columns(group_wise_columns, filters):
"fieldname": "currency",
"label" : _("Currency"),
"fieldtype": "Link",
"options": "Currency"
"options": "Currency",
"hidden": 1
})
return columns
@ -277,7 +278,7 @@ class GrossProfitGenerator(object):
from `tabPurchase Invoice Item` a
where a.item_code = %s and a.docstatus=1
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:
last_purchase_rate = frappe.db.sql("""
select (a.base_rate / a.conversion_factor)

View File

@ -102,7 +102,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
data.append(row)
if filters.get('group_by'):
if filters.get('group_by') and item_list:
total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
data.append(total_row)

View File

@ -111,7 +111,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
data.append(row)
if filters.get('group_by'):
if filters.get('group_by') and item_list:
total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
data.append(total_row)

View File

@ -44,9 +44,14 @@ def get_result(filters):
out = []
for supplier in filters.supplier:
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:
account = [d.account for d in tds.accounts if d.company == filters.company][0]
except IndexError:
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]))
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)
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])),
(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

View File

@ -817,48 +817,37 @@ def create_payment_gateway_account(gateway):
pass
@frappe.whitelist()
def update_number_field(doctype_name, name, field_name, number_value, company):
def update_cost_center(docname, cost_center_name, cost_center_number, company):
'''
doctype_name = Name of the DocType
name = Docname being referred
field_name = Name of the field thats holding the 'number' attribute
number_value = Numeric value entered in field_name
Stores the number entered in the dialog to the DocType's field.
Renames the document by adding the number as a prefix to the current name and updates
all transaction where it was present.
'''
doc_title = frappe.db.get_value(doctype_name, name, frappe.scrub(doctype_name)+"_name")
validate_field_number("Cost Center", docname, cost_center_number, company, "cost_center_number")
validate_field_number(doctype_name, name, number_value, company, field_name)
if cost_center_number:
frappe.db.set_value("Cost Center", docname, "cost_center_number", cost_center_number.strip())
else:
frappe.db.set_value("Cost Center", docname, "cost_center_number", "")
frappe.db.set_value(doctype_name, name, field_name, number_value)
frappe.db.set_value("Cost Center", docname, "cost_center_name", cost_center_name.strip())
if doc_title[0].isdigit():
separator = " - " if " - " in doc_title else " "
doc_title = doc_title.split(separator, 1)[1]
frappe.db.set_value(doctype_name, name, frappe.scrub(doctype_name)+"_name", doc_title)
new_name = get_autoname_with_number(number_value, doc_title, name, company)
if name != new_name:
frappe.rename_doc(doctype_name, name, new_name)
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
if docname != new_name:
frappe.rename_doc("Cost Center", docname, new_name, force=1)
return new_name
def validate_field_number(doctype_name, name, number_value, company, field_name):
def validate_field_number(doctype_name, docname, number_value, company, field_name):
''' Validate if the number entered isn't already assigned to some other document. '''
if number_value:
filters = {field_name: number_value, "name": ["!=", docname]}
if company:
doctype_with_same_number = frappe.db.get_value(doctype_name,
{field_name: number_value, "company": company, "name": ["!=", name]})
else:
doctype_with_same_number = frappe.db.get_value(doctype_name,
{field_name: number_value, "name": ["!=", name]})
filters["company"] = company
doctype_with_same_number = frappe.db.get_value(doctype_name, filters)
if doctype_with_same_number:
frappe.throw(_("{0} Number {1} already used in account {2}")
.format(doctype_name, number_value, doctype_with_same_number))
frappe.throw(_("{0} Number {1} is already used in {2} {3}")
.format(doctype_name, number_value, doctype_name.lower(), doctype_with_same_number))
def get_autoname_with_number(number_value, doc_title, name, company):
''' append title with prefix as number and suffix as company's abbreviation separated by '-' '''
@ -897,4 +886,60 @@ def get_stock_accounts(company):
return frappe.get_all("Account", filters = {
"account_type": "Stock",
"company": company
})
})
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None, company=None):
def _delete_gl_entries(voucher_type, voucher_no):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
for voucher_type, voucher_no in future_stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
if expected_gle:
if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
_delete_gl_entries(voucher_type, voucher_no)
voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
else:
_delete_gl_entries(voucher_type, voucher_no)
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
future_stock_vouchers = []
values = []
condition = ""
if for_items:
condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
values += for_items
if for_warehouses:
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
values += for_warehouses
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
tuple([posting_date, posting_time] + values), as_dict=True):
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
return future_stock_vouchers
def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
gl_entries = {}
if future_stock_vouchers:
for d in frappe.db.sql("""select * from `tabGL Entry`
where posting_date >= %s and voucher_no in (%s)""" %
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries

View File

@ -11,7 +11,7 @@ from frappe.model.document import Document
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_depreciation_accounts
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
from erpnext.accounts.utils import get_account_currency
from erpnext.controllers.accounts_controller import AccountsController
@ -41,7 +41,8 @@ class Asset(AccountsController):
self.validate_cancellation()
self.delete_depreciation_entries()
self.set_status()
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
make_reverse_gl_entries(voucher_type='Asset', voucher_no=self.name)
self.db_set('booked_fixed_asset', 0)
def validate_asset_and_reference(self):

View File

@ -66,9 +66,6 @@ class TestAsset(unittest.TestCase):
pr.cancel()
self.assertEqual(asset.docstatus, 2)
self.assertFalse(frappe.db.get_value("GL Entry",
{"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
def test_is_fixed_asset_set(self):
asset = create_asset(is_existing_asset = 1)
doc = frappe.new_doc('Purchase Invoice')

View File

@ -27,15 +27,6 @@ frappe.ui.form.on("Purchase Order", {
frm.set_indicator_formatter('item_code',
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() {
return {
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",
source_doctype: "Material Request",
target: me.frm,
setters: {
company: me.frm.doc.company
},
setters: {},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
@ -384,7 +373,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
source_doctype: "Supplier Quotation",
target: me.frm,
setters: {
company: me.frm.doc.company
supplier: me.frm.doc.supplier
},
get_query_filters: {
docstatus: 1,

View File

@ -54,11 +54,6 @@
"items_section",
"scan_barcode",
"items",
"section_break_48",
"pricing_rules",
"raw_material_details",
"set_reserve_warehouse",
"supplied_items",
"sb_last_purchase",
"total_qty",
"base_total",
@ -67,6 +62,11 @@
"total_net_weight",
"total",
"net_total",
"section_break_48",
"pricing_rules",
"raw_material_details",
"set_reserve_warehouse",
"supplied_items",
"taxes_section",
"tax_category",
"column_break_50",
@ -105,23 +105,25 @@
"payment_schedule_section",
"payment_terms_template",
"payment_schedule",
"tracking_section",
"per_billed",
"column_break_75",
"per_received",
"terms_section_break",
"tc_name",
"terms",
"more_info",
"status",
"ref_sq",
"column_break_74",
"party_account_currency",
"inter_company_order_reference",
"column_break_74",
"per_received",
"per_billed",
"column_break5",
"letter_head",
"select_print_heading",
"column_break_86",
"group_same_items",
"language",
"group_same_items",
"subscription_section",
"from_date",
"to_date",
@ -220,7 +222,7 @@
"allow_on_submit": 1,
"fieldname": "schedule_date",
"fieldtype": "Date",
"label": "Reqd By Date"
"label": "Required By"
},
{
"allow_on_submit": 1,
@ -432,6 +434,7 @@
"fieldtype": "Section Break"
},
{
"description": "Sets 'Warehouse' in each row of the Items table.",
"fieldname": "set_warehouse",
"fieldtype": "Link",
"label": "Set Target Warehouse",
@ -827,6 +830,7 @@
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "payment_schedule_section",
"fieldtype": "Section Break",
"label": "Payment Terms"
@ -917,7 +921,8 @@
"fieldname": "inter_company_order_reference",
"fieldtype": "Link",
"label": "Inter Company Order Reference",
"options": "Sales Order"
"options": "Sales Order",
"read_only": 1
},
{
"fieldname": "column_break_74",
@ -930,8 +935,6 @@
"in_list_view": 1,
"label": "% Received",
"no_copy": 1,
"oldfieldname": "per_received",
"oldfieldtype": "Currency",
"print_hide": 1,
"read_only": 1
},
@ -942,8 +945,6 @@
"in_list_view": 1,
"label": "% Billed",
"no_copy": 1,
"oldfieldname": "per_billed",
"oldfieldtype": "Currency",
"print_hide": 1,
"read_only": 1
},
@ -998,6 +999,7 @@
"print_hide": 1
},
{
"collapsible": 1,
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"label": "Subscription Section"
@ -1050,13 +1052,23 @@
"fieldtype": "Link",
"label": "Set Reserve 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",
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2020-04-17 13:04:28.185197",
"modified": "2020-04-24 12:13:14.186280",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@ -18,10 +18,6 @@
"col_break1",
"image",
"image_view",
"manufacture_details",
"manufacturer",
"column_break_14",
"manufacturer_part_no",
"quantity_and_rate",
"qty",
"stock_uom",
@ -44,7 +40,6 @@
"base_amount",
"pricing_rules",
"is_free_item",
"is_fixed_asset",
"section_break_29",
"net_rate",
"net_amount",
@ -52,11 +47,6 @@
"base_net_rate",
"base_net_amount",
"billed_amt",
"item_weight_details",
"weight_per_unit",
"total_weight",
"column_break_40",
"weight_uom",
"warehouse_and_reference",
"warehouse",
"delivered_by_supplier",
@ -80,20 +70,31 @@
"column_break_60",
"received_qty",
"returned_qty",
"manufacture_details",
"manufacturer",
"column_break_14",
"manufacturer_part_no",
"more_info_section_break",
"is_fixed_asset",
"item_tax_rate",
"accounting_details",
"expense_account",
"column_break_68",
"item_weight_details",
"weight_per_unit",
"total_weight",
"column_break_40",
"weight_uom",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"section_break_72",
"page_break",
"item_tax_rate"
"page_break"
],
"fields": [
{
"bold": 1,
"columns": 3,
"columns": 2,
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
@ -133,7 +134,7 @@
"fieldname": "schedule_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Reqd By Date",
"label": "Required By",
"oldfieldname": "schedule_date",
"oldfieldtype": "Date",
"print_hide": 1,
@ -216,15 +217,16 @@
"print_hide": 1
},
{
"columns": 1,
"fieldname": "uom",
"fieldtype": "Link",
"in_list_view": 1,
"label": "UOM",
"oldfieldname": "uom",
"oldfieldtype": "Link",
"options": "UOM",
"print_width": "100px",
"reqd": 1,
"width": "100px"
"reqd": 1
},
{
"fieldname": "conversion_factor",
@ -685,6 +687,7 @@
"fieldtype": "Column Break"
},
{
"collapsible": 1,
"fieldname": "manufacture_details",
"fieldtype": "Section Break",
"label": "Manufacture"
@ -717,12 +720,17 @@
"fieldtype": "Check",
"label": "Is Fixed Asset",
"read_only": 1
},
{
"fieldname": "more_info_section_break",
"fieldtype": "Section Break",
"label": "More Information"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-04-07 18:35:17.558928",
"modified": "2020-04-21 11:55:58.643393",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

View File

@ -1,20 +1,26 @@
{
"actions": [],
"creation": "2013-02-22 01:27:42",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"main_item_code",
"rm_item_code",
"required_qty",
"supplied_qty",
"rate",
"amount",
"column_break_6",
"bom_detail_no",
"reference_name",
"conversion_factor",
"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": [
{
@ -120,15 +126,34 @@
"in_list_view": 1,
"label": "Supplied Qty",
"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,
"idx": 1,
"istable": 1,
"modified": "2019-08-20 13:37:32.702068",
"links": [],
"modified": "2020-03-12 15:43:53.862897",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item Supplied",
"owner": "dhanalekshmi@webnotestech.com",
"permissions": []
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -23,15 +23,11 @@ def get_data():
},
{
'label': _('Payments'),
'items': ['Payment Entry']
},
{
'label': _('Bank'),
'items': ['Bank Account']
'items': ['Payment Entry', 'Bank Account']
},
{
'label': _('Pricing'),
'items': ['Pricing Rule']
}
]
}
}

View File

@ -141,19 +141,18 @@ def get_conditions(filters):
conditions = ""
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"):
conditions += """
AND (cost_center=%s
OR project=%s)
"""% (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
AND (child.`cost_center`=%s OR child.`project`=%s)
""" % (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
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"):
conditions += " AND transaction_date<=%s"% filters.get('to_date')
conditions += " AND par.transaction_date<='%s'" % filters.get('to_date')
return conditions
def get_data(filters):
@ -162,7 +161,6 @@ def get_data(filters):
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
pr_records = get_mapped_pr_records()
pi_records = get_mapped_pi_records()
print(pi_records)
procurement_record=[]
if procurement_record_against_mr:
@ -198,16 +196,16 @@ def get_mapped_mr_details(conditions):
mr_records = {}
mr_details = frappe.db.sql("""
SELECT
mr.transaction_date,
mr.per_ordered,
mr_item.name,
mr_item.parent,
mr_item.amount
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
par.transaction_date,
par.per_ordered,
child.name,
child.parent,
child.amount
FROM `tabMaterial Request` par, `tabMaterial Request Item` child
WHERE
mr.per_ordered>=0
AND mr.name=mr_item.parent
AND mr.docstatus=1
par.per_ordered>=0
AND par.name=child.parent
AND par.docstatus=1
{conditions}
""".format(conditions=conditions), as_dict=1) #nosec
@ -254,29 +252,29 @@ def get_mapped_pr_records():
def get_po_entries(conditions):
return frappe.db.sql("""
SELECT
po_item.name,
po_item.parent,
po_item.cost_center,
po_item.project,
po_item.warehouse,
po_item.material_request,
po_item.material_request_item,
po_item.description,
po_item.stock_uom,
po_item.qty,
po_item.amount,
po_item.base_amount,
po_item.schedule_date,
po.transaction_date,
po.supplier,
po.status,
po.owner
FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item
child.name,
child.parent,
child.cost_center,
child.project,
child.warehouse,
child.material_request,
child.material_request_item,
child.description,
child.stock_uom,
child.qty,
child.amount,
child.base_amount,
child.schedule_date,
par.transaction_date,
par.supplier,
par.status,
par.owner
FROM `tabPurchase Order` par, `tabPurchase Order Item` child
WHERE
po.docstatus = 1
AND po.name = po_item.parent
AND po.status not in ("Closed","Completed","Cancelled")
par.docstatus = 1
AND par.name = child.parent
AND par.status not in ("Closed","Completed","Cancelled")
{conditions}
GROUP BY
po.name,po_item.item_code
par.name, child.item_code
""".format(conditions=conditions), as_dict=1) #nosec

View File

@ -170,6 +170,10 @@ def get_data():
"type": "doctype",
"name": "Payroll Period",
},
{
"type": "doctype",
"name": "Income Tax Slab",
},
{
"type": "doctype",
"name": "Salary Component",
@ -209,6 +213,10 @@ def get_data():
"name": "Employee Tax Exemption Proof Submission",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Other Income",
},
{
"type": "doctype",
"name": "Employee Benefit Application",

View File

@ -664,23 +664,26 @@ class AccountsController(TransactionBase):
def set_total_advance_paid(self):
if self.doctype == "Sales Order":
dr_or_cr = "credit_in_account_currency"
rev_dr_or_cr = "debit_in_account_currency"
party = self.customer
else:
dr_or_cr = "debit_in_account_currency"
rev_dr_or_cr = "credit_in_account_currency"
party = self.supplier
advance = frappe.db.sql("""
select
account_currency, sum({dr_or_cr}) as amount
account_currency, sum({dr_or_cr}) - sum({rev_dr_cr}) as amount
from
`tabGL Entry`
where
against_voucher_type = %s and against_voucher = %s and party=%s
and docstatus = 1
""".format(dr_or_cr=dr_or_cr), (self.doctype, self.name, party), as_dict=1)
""".format(dr_or_cr=dr_or_cr, rev_dr_cr=rev_dr_or_cr), (self.doctype, self.name, party), as_dict=1) #nosec
if advance:
advance = advance[0]
advance_paid = flt(advance.amount, self.precision("advance_paid"))
formatted_advance_paid = fmt_money(advance_paid, precision=self.precision("advance_paid"),
currency=advance.account_currency)

View File

@ -371,6 +371,19 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters):
fields = ["name", "parent_account"],
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()
def get_income_account(doctype, txt, searchfield, start, page_len, filters):

View File

@ -3,11 +3,11 @@
from __future__ import unicode_literals
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 _
import frappe.defaults
from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
@ -23,9 +23,9 @@ class StockController(AccountsController):
self.validate_serialized_batch()
self.validate_customer_provided_item()
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
def make_gl_entries(self, gl_entries=None):
if self.docstatus == 2:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(erpnext.is_perpetual_inventory_enabled(self.company)):
warehouse_account = get_warehouse_account_map(self.company)
@ -33,16 +33,12 @@ class StockController(AccountsController):
if self.docstatus==1:
if not gl_entries:
gl_entries = self.get_gl_entries(warehouse_account)
make_gl_entries(gl_entries, from_repost=from_repost)
make_gl_entries(gl_entries)
if (repost_future_gle or self.flags.repost_future_gle):
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
warehouse_account, company=self.company)
elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1:
gl_entries = []
gl_entries = self.get_asset_gl_entry(gl_entries)
make_gl_entries(gl_entries, from_repost=from_repost)
make_gl_entries(gl_entries)
def validate_serialized_batch(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@ -55,6 +51,13 @@ class StockController(AccountsController):
frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
.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,
default_cost_center=None):
@ -267,21 +270,21 @@ class StockController(AccountsController):
"batch_no": cstr(d.get("batch_no")).strip(),
"serial_no": d.get("serial_no"),
"project": d.get("project") or self.get('project'),
"is_cancelled": self.docstatus==2 and "Yes" or "No"
"is_cancelled": 1 if self.docstatus==2 else 0
})
sl_dict.update(args)
return sl_dict
def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False,
def make_sl_entries(self, sl_entries, allow_negative_stock=False,
via_landed_cost_voucher=False):
from erpnext.stock.stock_ledger import make_sl_entries
make_sl_entries(sl_entries, is_amended, allow_negative_stock, via_landed_cost_voucher)
make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher)
def make_gl_entries_on_cancel(self, repost_future_gle=True):
def make_gl_entries_on_cancel(self):
if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
and voucher_no=%s""", (self.doctype, self.name)):
self.make_gl_entries(repost_future_gle=repost_future_gle)
self.make_gl_entries()
def get_serialized_items(self):
serialized_items = []
@ -384,29 +387,6 @@ class StockController(AccountsController):
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None, company=None):
def _delete_gl_entries(voucher_type, voucher_no):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
for voucher_type, voucher_no in future_stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
if expected_gle:
if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
_delete_gl_entries(voucher_type, voucher_no)
voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
else:
_delete_gl_entries(voucher_type, voucher_no)
def compare_existing_and_expected_gle(existing_gle, expected_gle):
matched = True
for entry in expected_gle:
@ -423,36 +403,3 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle):
matched = False
break
return matched
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
future_stock_vouchers = []
values = []
condition = ""
if for_items:
condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
values += for_items
if for_warehouses:
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
values += for_warehouses
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
tuple([posting_date, posting_time] + values), as_dict=True):
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
return future_stock_vouchers
def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
gl_entries = {}
if future_stock_vouchers:
for d in frappe.db.sql("""select * from `tabGL Entry`
where posting_date >= %s and voucher_no in (%s)""" %
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries

View File

@ -12,13 +12,18 @@
},
{
"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\": \"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]"
"label": "Maintenance",
"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,
"label": "Maintenance",
"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]"
"label": "Campaign",
"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",
@ -33,7 +38,7 @@
"idx": 0,
"is_standard": 1,
"label": "CRM",
"modified": "2020-04-01 11:28:51.219999",
"modified": "2020-04-27 22:32:26.682911",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",
@ -42,7 +47,7 @@
"pin_to_top": 0,
"shortcuts": [
{
"format": "Open",
"format": "{} Open",
"label": "Lead",
"link_to": "Lead",
"stats_filter": "{\"status\":\"Open\"}",

View File

@ -16,23 +16,27 @@ frappe.ui.form.on('Twitter Settings', {
}
},
refresh: function(frm){
let msg,color;
let msg, color, flag=false;
if (frm.doc.session_status == "Active"){
msg = __("Session Active");
color = 'green';
flag = true;
}
else {
else if(frm.doc.consumer_key && frm.doc.consumer_secret) {
msg = __("Session Not Active. Save doc to login.");
color = 'red';
flag = true;
}
frm.dashboard.set_headline_alert(
`<div class="row">
<div class="col-xs-12">
<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">${msg}</span></span>
</div>
</div>`
);
if (flag){
frm.dashboard.set_headline_alert(
`<div class="row">
<div class="col-xs-12">
<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">${msg}</span></span>
</div>
</div>`
);
}
},
login: function(frm){
if (frm.doc.consumer_key && frm.doc.consumer_secret){

24
erpnext/crm/utils.py Normal file
View File

@ -0,0 +1,24 @@
import frappe
def update_lead_phone_numbers(contact, method):
if contact.phone_nos:
contact_lead = contact.get_link_for("Lead")
if contact_lead:
phone = mobile_no = contact.phone_nos[0].phone
if len(contact.phone_nos) > 1:
# get the default phone number
primary_phones = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_phone]
if primary_phones:
phone = primary_phones[0]
# get the default mobile number
primary_mobile_nos = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_mobile_no]
if primary_mobile_nos:
mobile_no = primary_mobile_nos[0]
lead = frappe.get_doc("Lead", contact_lead)
lead.phone = phone
lead.mobile_no = mobile_no
lead.save()

View File

@ -112,6 +112,8 @@ frappe.ui.form.on("Fees", {
args: {
"dt": frm.doc.doctype,
"dn": frm.doc.name,
"party_type": "Student",
"party": frm.doc.student,
"recipient_id": frm.doc.student_email
},
callback: function(r) {

View File

@ -10,7 +10,7 @@ from frappe.utils import money_in_words
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from frappe.utils.csvutils import getlink
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import delete_gl_entries
from erpnext.accounts.general_ledger import make_reverse_gl_entries
class Fees(AccountsController):
@ -75,12 +75,14 @@ class Fees(AccountsController):
self.make_gl_entries()
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)
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
def on_cancel(self):
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name, cancel=True)
# frappe.db.set(self, 'status', 'Cancelled')

View File

@ -6,6 +6,9 @@ def get_data():
'heatmap': True,
'heatmap_message': _('This is based on the attendance of this Student'),
'fieldname': 'student',
'non_standard_fieldnames': {
'Bank Account': 'party'
},
'transactions': [
{
'label': _('Admission'),
@ -29,7 +32,7 @@ def get_data():
},
{
'label': _('Fee'),
'items': ['Fees']
'items': ['Fees', 'Bank Account']
}
]
}
}

View File

@ -80,6 +80,7 @@ frappe.ui.form.on('Clinical Procedure', {
frappe.call({
method: 'complete_procedure',
doc: frm.doc,
freeze: true,
callback: function(r) {
if (r.message) {
frappe.show_alert({
@ -87,8 +88,8 @@ frappe.ui.form.on('Clinical Procedure', {
['<a class="bold" href="#Form/Stock Entry/'+ r.message + '">' + r.message + '</a>']),
indicator: 'green'
});
frm.reload_doc();
}
frm.reload_doc();
}
});
}
@ -111,9 +112,10 @@ frappe.ui.form.on('Clinical Procedure', {
frappe.call({
doc: frm.doc,
method: 'make_material_receipt',
freeze: true,
callback: function(r) {
if (!r.exc) {
cur_frm.reload_doc();
frm.reload_doc();
let doclist = frappe.model.sync(r.message);
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
}
@ -122,7 +124,7 @@ frappe.ui.form.on('Clinical Procedure', {
}
);
} else {
cur_frm.reload_doc();
frm.reload_doc();
}
}
}

View File

@ -87,7 +87,8 @@ class ClinicalProcedure(Document):
else:
frappe.throw(_('Please set Customer in Patient {0}').format(frappe.bold(self.patient)), title=_('Customer Not Found'))
frappe.db.set_value('Clinical Procedure', self.name, 'status', 'Completed')
self.db_set('status', 'Completed')
if self.consume_stock and self.items:
return stock_entry
@ -245,9 +246,9 @@ def make_procedure(source_name, target_doc=None):
def insert_clinical_procedure_to_medical_record(doc):
subject = cstr(doc.procedure_template)
subject = frappe.bold(_("Clinical Procedure conducted: ")) + cstr(doc.procedure_template) + "<br>"
if doc.practitioner:
subject += ' ' + doc.practitioner
subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner
if subject and doc.notes:
subject += '<br/>' + doc.notes

View File

@ -24,6 +24,8 @@ erpnext.ExerciseEditor = Class.extend({
this.exercise_cards = $('<div class="exercise-cards"></div>').appendTo(this.wrapper);
this.row = $('<div class="exercise-row"></div>').appendTo(this.wrapper);
let me = this;
this.exercise_toolbar.find(".btn-add")
@ -32,7 +34,7 @@ erpnext.ExerciseEditor = Class.extend({
me.show_add_card_dialog(frm);
});
if (frm.doc.steps_table.length > 0) {
if (frm.doc.steps_table && frm.doc.steps_table.length > 0) {
this.make_cards(frm);
this.make_buttons(frm);
}
@ -41,7 +43,6 @@ erpnext.ExerciseEditor = Class.extend({
make_cards: function(frm) {
var me = this;
$(me.exercise_cards).empty();
this.row = $('<div class="exercise-row"></div>').appendTo(me.exercise_cards);
$.each(frm.doc.steps_table, function(i, step) {
$(repl(`
@ -78,6 +79,7 @@ erpnext.ExerciseEditor = Class.extend({
frm.doc.steps_table.pop(id);
frm.refresh_field('steps_table');
$('#col-'+id).remove();
frm.dirty();
}, 300);
});
},
@ -106,7 +108,10 @@ erpnext.ExerciseEditor = Class.extend({
],
primary_action: function() {
let data = d.get_values();
let i = frm.doc.steps_table.length;
let i = 0;
if (frm.doc.steps_table) {
i = frm.doc.steps_table.length;
}
$(repl(`
<div class="exercise-col col-sm-4" id="%(col_id)s">
<div class="card h-100 exercise-card" id="%(card_id)s">
@ -165,9 +170,10 @@ erpnext.ExerciseEditor = Class.extend({
frm.doc.steps_table[id].image = data.image;
frm.doc.steps_table[id].description = data.step_description;
refresh_field('steps_table');
frm.dirty();
new_dialog.hide();
},
primary_action_label: __("Save"),
primary_action_label: __("Edit"),
});
new_dialog.set_values({

View File

@ -288,23 +288,23 @@ def insert_lab_test_to_medical_record(doc):
table_row = False
subject = cstr(doc.lab_test_name)
if doc.practitioner:
subject += " "+ doc.practitioner
subject += frappe.bold(_("Healthcare Practitioner: "))+ doc.practitioner + "<br>"
if doc.normal_test_items:
item = doc.normal_test_items[0]
comment = ""
if item.lab_test_comment:
comment = str(item.lab_test_comment)
table_row = item.lab_test_name
table_row = frappe.bold(_("Lab Test Conducted: ")) + item.lab_test_name
if item.lab_test_event:
table_row += " " + item.lab_test_event
table_row += frappe.bold(_("Lab Test Event: ")) + item.lab_test_event
if item.result_value:
table_row += " " + item.result_value
table_row += " " + frappe.bold(_("Lab Test Result: ")) + item.result_value
if item.normal_range:
table_row += " normal_range("+item.normal_range+")"
table_row += " "+comment
table_row += " " + _("Normal Range:") + item.normal_range
table_row += " " + comment
elif doc.special_test_items:
item = doc.special_test_items[0]
@ -316,12 +316,12 @@ def insert_lab_test_to_medical_record(doc):
item = doc.sensitivity_test_items[0]
if item.antibiotic and item.antibiotic_sensitivity:
table_row = item.antibiotic +" "+ item.antibiotic_sensitivity
table_row = item.antibiotic + " " + item.antibiotic_sensitivity
if table_row:
subject += "<br/>"+table_row
subject += "<br>" + table_row
if doc.lab_test_comment:
subject += "<br/>"+ cstr(doc.lab_test_comment)
subject += "<br>" + cstr(doc.lab_test_comment)
medical_record = frappe.new_doc("Patient Medical Record")
medical_record.patient = doc.patient

View File

@ -18,6 +18,9 @@ class PatientEncounter(Document):
def after_insert(self):
insert_encounter_to_medical_record(self)
def on_submit(self):
update_encounter_medical_record(self)
def on_cancel(self):
if self.appointment:
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
@ -66,22 +69,26 @@ def delete_medical_record(encounter):
frappe.db.delete_doc_if_exists('Patient Medical Record', 'reference_name', encounter.name)
def set_subject_field(encounter):
subject = encounter.practitioner + '\n'
subject = frappe.bold(_('Healthcare Practitioner: ')) + encounter.practitioner + '<br>'
if encounter.symptoms:
subject += _('Symptoms: ') + cstr(encounter.symptoms) + '\n'
subject += frappe.bold(_('Symptoms: ')) + '<br>'
for entry in encounter.symptoms:
subject += cstr(entry.complaint) + '<br>'
else:
subject += _('No Symptoms') + '\n'
subject += frappe.bold(_('No Symptoms')) + '<br>'
if encounter.diagnosis:
subject += _('Diagnosis: ') + cstr(encounter.diagnosis) + '\n'
subject += frappe.bold(_('Diagnosis: ')) + '<br>'
for entry in encounter.diagnosis:
subject += cstr(entry.diagnosis) + '<br>'
else:
subject += _('No Diagnosis') + '\n'
subject += frappe.bold(_('No Diagnosis')) + '<br>'
if encounter.drug_prescription:
subject += '\n' + _('Drug(s) Prescribed.')
subject += '<br>' + _('Drug(s) Prescribed.')
if encounter.lab_test_prescription:
subject += '\n' + _('Test(s) Prescribed.')
subject += '<br>' + _('Test(s) Prescribed.')
if encounter.procedure_prescription:
subject += '\n' + _('Procedure(s) Prescribed.')
subject += '<br>' + _('Procedure(s) Prescribed.')
return subject

View File

@ -57,7 +57,7 @@
},
{
"fieldname": "subject",
"fieldtype": "Small Text",
"fieldtype": "Text Editor",
"ignore_xss_filter": 1,
"label": "Subject"
},
@ -125,7 +125,7 @@
],
"in_create": 1,
"links": [],
"modified": "2020-03-23 19:26:59.308383",
"modified": "2020-04-29 12:26:57.679402",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Medical Record",

View File

@ -21,8 +21,14 @@ class TherapyPlan(Document):
self.status = 'Completed'
def set_totals(self):
total_sessions = sum([int(d.no_of_sessions) for d in self.get('therapy_plan_details')])
total_sessions_completed = sum([int(d.sessions_completed) for d in self.get('therapy_plan_details')])
total_sessions = 0
total_sessions_completed = 0
for entry in self.therapy_plan_details:
if entry.no_of_sessions:
total_sessions += entry.no_of_sessions
if entry.sessions_completed:
total_sessions_completed += entry.sessions_completed
self.db_set('total_sessions', total_sessions)
self.db_set('total_sessions_completed', total_sessions_completed)

View File

@ -13,23 +13,92 @@ frappe.ui.form.on('Therapy Session', {
refresh: function(frm) {
if (!frm.doc.__islocal) {
let target = 0;
let completed = 0;
$.each(frm.doc.exercises, function(_i, e) {
target += e.counts_target;
completed += e.counts_completed;
});
frm.dashboard.add_indicator(__('Counts Targetted: {0}', [target]), 'blue');
frm.dashboard.add_indicator(__('Counts Completed: {0}', [completed]), (completed < target) ? 'orange' : 'green');
frm.dashboard.add_indicator(__('Counts Targeted: {0}', [frm.doc.total_counts_targeted]), 'blue');
frm.dashboard.add_indicator(__('Counts Completed: {0}', [frm.doc.total_counts_completed]),
(frm.doc.total_counts_completed < frm.doc.total_counts_targeted) ? 'orange' : 'green');
}
if (frm.doc.docstatus === 1) {
frm.add_custom_button(__('Patient Assessment'),function() {
frm.add_custom_button(__('Patient Assessment'), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.healthcare.doctype.patient_assessment.patient_assessment.create_patient_assessment',
frm: frm,
})
}, 'Create');
frm.add_custom_button(__('Sales Invoice'), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.invoice_therapy_session',
frm: frm,
})
}, 'Create');
}
},
patient: function(frm) {
if (frm.doc.patient) {
frappe.call({
'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: frm.doc.patient
},
callback: function (data) {
let age = '';
if (data.message.dob) {
age = calculate_age(data.message.dob);
} else if (data.message.age) {
age = data.message.age;
if (data.message.age_as_on) {
age = __('{0} as on {1}', [age, data.message.age_as_on]);
}
}
frm.set_value('patient_age', age);
frm.set_value('gender', data.message.sex);
frm.set_value('patient_name', data.message.patient_name);
}
});
} else {
frm.set_value('patient_age', '');
frm.set_value('gender', '');
frm.set_value('patient_name', '');
}
},
appointment: function(frm) {
if (frm.doc.appointment) {
frappe.call({
'method': 'frappe.client.get',
args: {
doctype: 'Patient Appointment',
name: frm.doc.appointment
},
callback: function(data) {
let values = {
'patient':data.message.patient,
'therapy_type': data.message.therapy_type,
'therapy_plan': data.message.therapy_plan,
'practitioner': data.message.practitioner,
'department': data.message.department,
'start_date': data.message.appointment_date,
'start_time': data.message.appointment_time,
'service_unit': data.message.service_unit,
'company': data.message.company
};
frm.set_value(values);
}
});
} else {
let values = {
'patient': '',
'therapy_type': '',
'therapy_plan': '',
'practitioner': '',
'department': '',
'start_date': '',
'start_time': '',
'service_unit': '',
};
frm.set_value(values);
}
},
@ -44,6 +113,8 @@ frappe.ui.form.on('Therapy Session', {
callback: function(data) {
frm.set_value('duration', data.message.default_duration);
frm.set_value('rate', data.message.rate);
frm.set_value('service_unit', data.message.healthcare_service_unit);
frm.set_value('department', data.message.medical_department);
frm.doc.exercises = [];
$.each(data.message.exercises, function(_i, e) {
let exercise = frm.add_child('exercises');

View File

@ -9,9 +9,11 @@
"naming_series",
"appointment",
"patient",
"patient_name",
"patient_age",
"gender",
"column_break_5",
"company",
"therapy_plan",
"therapy_type",
"practitioner",
@ -20,7 +22,6 @@
"duration",
"rate",
"location",
"company",
"column_break_12",
"service_unit",
"start_date",
@ -28,6 +29,10 @@
"invoiced",
"exercises_section",
"exercises",
"section_break_23",
"total_counts_targeted",
"column_break_25",
"total_counts_completed",
"amended_from"
],
"fields": [
@ -159,7 +164,8 @@
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company"
"options": "Company",
"reqd": 1
},
{
"default": "0",
@ -173,11 +179,38 @@
"fieldtype": "Data",
"label": "Patient Age",
"read_only": 1
},
{
"fieldname": "total_counts_targeted",
"fieldtype": "Int",
"label": "Total Counts Targeted",
"read_only": 1
},
{
"fieldname": "total_counts_completed",
"fieldtype": "Int",
"label": "Total Counts Completed",
"read_only": 1
},
{
"fieldname": "section_break_23",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_25",
"fieldtype": "Column Break"
},
{
"fetch_from": "patient.patient_name",
"fieldname": "patient_name",
"fieldtype": "Data",
"label": "Patient Name",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-04-21 13:16:46.378798",
"modified": "2020-04-29 16:49:16.286006",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Therapy Session",

View File

@ -6,10 +6,17 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe import _
from frappe.utils import cstr, getdate
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
class TherapySession(Document):
def validate(self):
self.set_total_counts()
def on_submit(self):
self.update_sessions_count_in_therapy_plan()
insert_session_medical_record(self)
def on_cancel(self):
self.update_sessions_count_in_therapy_plan(on_cancel=True)
@ -24,6 +31,18 @@ class TherapySession(Document):
entry.sessions_completed += 1
therapy_plan.save()
def set_total_counts(self):
target_total = 0
counts_completed = 0
for entry in self.exercises:
if entry.counts_target:
target_total += entry.counts_target
if entry.counts_completed:
counts_completed += entry.counts_completed
self.db_set('total_counts_targeted', target_total)
self.db_set('total_counts_completed', counts_completed)
@frappe.whitelist()
def create_therapy_session(source_name, target_doc=None):
@ -52,4 +71,62 @@ def create_therapy_session(source_name, target_doc=None):
}
}, target_doc, set_missing_values)
return doc
return doc
@frappe.whitelist()
def invoice_therapy_session(source_name, target_doc=None):
def set_missing_values(source, target):
target.customer = frappe.db.get_value('Patient', source.patient, 'customer')
target.due_date = getdate()
target.debit_to = get_receivable_account(source.company)
item = target.append('items', {})
item = get_therapy_item(source, item)
target.set_missing_values(for_validate=True)
doc = get_mapped_doc('Therapy Session', source_name, {
'Therapy Session': {
'doctype': 'Sales Invoice',
'field_map': [
['patient', 'patient'],
['referring_practitioner', 'practitioner'],
['company', 'company'],
['due_date', 'start_date']
]
}
}, target_doc, set_missing_values)
return doc
def get_therapy_item(therapy, item):
item.item_code = frappe.db.get_value('Therapy Type', therapy.therapy_type, 'item')
item.description = _('Therapy Session Charges: {0}').format(therapy.practitioner)
item.income_account = get_income_account(therapy.practitioner, therapy.company)
item.cost_center = frappe.get_cached_value('Company', therapy.company, 'cost_center')
item.rate = therapy.rate
item.amount = therapy.rate
item.qty = 1
item.reference_dt = 'Therapy Session'
item.reference_dn = therapy.name
return item
def insert_session_medical_record(doc):
subject = frappe.bold(_('Therapy: ')) + cstr(doc.therapy_type) + '<br>'
if doc.therapy_plan:
subject += frappe.bold(_('Therapy Plan: ')) + cstr(doc.therapy_plan) + '<br>'
if doc.practitioner:
subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner
subject += frappe.bold(_('Total Counts Targeted: ')) + cstr(doc.total_counts_targeted) + '<br>'
subject += frappe.bold(_('Total Counts Completed: ')) + cstr(doc.total_counts_completed) + '<br>'
medical_record = frappe.new_doc('Patient Medical Record')
medical_record.patient = doc.patient
medical_record.subject = subject
medical_record.status = 'Open'
medical_record.communication_date = doc.start_date
medical_record.reference_doctype = 'Therapy Session'
medical_record.reference_name = doc.name
medical_record.reference_owner = doc.owner
medical_record.save(ignore_permissions=True)

View File

@ -35,17 +35,17 @@ def delete_vital_signs_from_medical_record(doc):
def set_subject_field(doc):
subject = ''
if(doc.temperature):
subject += _('Temperature: ') + '\n'+ cstr(doc.temperature) + '. '
if(doc.pulse):
subject += _('Pulse: ') + '\n' + cstr(doc.pulse) + '. '
if(doc.respiratory_rate):
subject += _('Respiratory Rate: ') + '\n' + cstr(doc.respiratory_rate) + '. '
if(doc.bp):
subject += _('BP: ') + '\n' + cstr(doc.bp) + '. '
if(doc.bmi):
subject += _('BMI: ') + '\n' + cstr(doc.bmi) + '. '
if(doc.nutrition_note):
subject += _('Note: ') + '\n' + cstr(doc.nutrition_note) + '. '
if doc.temperature:
subject += frappe.bold(_('Temperature: ')) + cstr(doc.temperature) + '<br>'
if doc.pulse:
subject += frappe.bold(_('Pulse: ')) + cstr(doc.pulse) + '<br>'
if doc.respiratory_rate:
subject += frappe.bold(_('Respiratory Rate: ')) + cstr(doc.respiratory_rate) + '<br>'
if doc.bp:
subject += frappe.bold(_('BP: ')) + cstr(doc.bp) + '<br>'
if doc.bmi:
subject += frappe.bold(_('BMI: ')) + cstr(doc.bmi) + '<br>'
if doc.nutrition_note:
subject += frappe.bold(_('Note: ')) + cstr(doc.nutrition_note) + '<br>'
return subject

View File

@ -43,7 +43,7 @@ def validate_customer_created(patient):
def get_fee_validity(patient_appointments):
if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
return
return []
items_to_invoice = []
for appointment in patient_appointments:
@ -110,7 +110,7 @@ def get_lab_tests_to_invoice(patient):
filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
)
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:
lab_tests_to_invoice.append({
'reference_type': 'Lab Test',

View File

@ -250,7 +250,8 @@ doc_events = {
},
"Contact": {
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information",
"validate": "erpnext.crm.utils.update_lead_phone_numbers"
},
"Lead": {
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
@ -537,4 +538,4 @@ global_search_doctypes = {
{'doctype': 'Hotel Room Package', 'index': 3},
{'doctype': 'Hotel Room Type', 'index': 4}
]
}
}

View File

@ -23,7 +23,7 @@
{
"hidden": 0,
"label": "Payroll",
"links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]"
"links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@ -73,7 +73,7 @@
{
"hidden": 0,
"label": "Employee Tax and Benefits",
"links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]"
"links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\",\n \"Payroll Period\"\n ],\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Modules",
@ -88,7 +88,7 @@
"idx": 0,
"is_standard": 1,
"label": "HR",
"modified": "2020-04-01 11:28:50.860012",
"modified": "2020-04-29 20:29:22.114309",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",

View File

@ -87,11 +87,12 @@
"search_index": 1
},
{
"depends_on": "eval:doc.status==\"On Leave\"",
"depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
"fieldname": "leave_type",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Leave Type",
"mandatory_depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
"oldfieldname": "leave_type",
"oldfieldtype": "Link",
"options": "Leave Type"
@ -100,6 +101,7 @@
"fieldname": "leave_application",
"fieldtype": "Link",
"label": "Leave Application",
"no_copy": 1,
"options": "Leave Application",
"read_only": 1
},
@ -175,7 +177,8 @@
"icon": "fa fa-ok",
"idx": 1,
"is_submittable": 1,
"modified": "2020-02-19 14:25:32.945842",
"links": [],
"modified": "2020-04-11 11:40:14.319496",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",

View File

@ -7,33 +7,15 @@ import frappe
from frappe.utils import getdate, nowdate
from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, get_datetime, get_datetime_str
from frappe.utils import update_progress_bar
from frappe.utils import cstr, get_datetime, formatdate
class Attendance(Document):
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:
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(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()
def validate_attendance_date(self):
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):
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):
emp = frappe.db.sql("select name from `tabEmployee` where name = %s and status = 'Active'",
self.employee)
if not emp:
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()
def get_events(start, end, filters=None):
events = []
@ -90,18 +105,20 @@ def add_attendance(events, start, end, conditions=None):
if e not in events:
events.append(e)
def mark_attendance(employee, attendance_date, status, shift=None):
employee_doc = frappe.get_doc('Employee', employee)
def mark_attendance(employee, attendance_date, status, shift=None, leave_type=None, ignore_validate=False):
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',
'employee': employee,
'attendance_date': attendance_date,
'status': status,
'company': employee_doc.company,
'shift': shift
}
attendance = frappe.get_doc(doc_dict).insert()
'company': company,
'shift': shift,
'leave_type': leave_type
})
attendance.flags.ignore_validate = ignore_validate
attendance.insert()
attendance.submit()
return attendance.name

View File

@ -6,6 +6,9 @@ def get_data():
'heatmap': True,
'heatmap_message': _('This is based on the attendance of this Employee'),
'fieldname': 'employee',
'non_standard_fieldnames': {
'Bank Account': 'party'
},
'transactions': [
{
'label': _('Leave and Attendance'),
@ -33,7 +36,7 @@ def get_data():
},
{
'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'),

View File

@ -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) {
// }
});

View File

@ -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
}

View 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 EmployeeOtherIncome(Document):
pass

View 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 TestEmployeeOtherIncome(unittest.TestCase):
pass

View File

@ -1,620 +1,180 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "HR-TAX-DEC-.YYYY.-.#####",
"beta": 0,
"creation": "2018-04-13 16:53:36.175504",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "HR-TAX-DEC-.YYYY.-.#####",
"creation": "2018-04-13 16:53:36.175504",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee",
"employee_name",
"department",
"column_break_2",
"payroll_period",
"company",
"amended_from",
"section_break_8",
"declarations",
"section_break_10",
"total_declared_amount",
"column_break_12",
"total_exemption_amount"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
},
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"label": "Employee Name",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"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
},
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"fieldname": "payroll_period",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Payroll Period",
"options": "Payroll Period",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
},
"fetch_from": "employee.company",
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"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
},
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Employee Tax Exemption Declaration",
"print_hide": 1,
"read_only": 1
},
{
"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_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
},
"fieldname": "section_break_8",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
},
"fieldname": "declarations",
"fieldtype": "Table",
"label": "Declarations",
"options": "Employee Tax Exemption Declaration Category"
},
{
"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_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
},
"fieldname": "section_break_10",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
},
"fieldname": "total_declared_amount",
"fieldtype": "Currency",
"label": "Total Declared Amount",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"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
},
"fieldname": "column_break_12",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
"fieldname": "total_exemption_amount",
"fieldtype": "Currency",
"label": "Total Exemption Amount",
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"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",
],
"is_submittable": 1,
"links": [],
"modified": "2020-03-18 14:56:25.625717",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Tax Exemption Declaration",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 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
},
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 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
},
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -8,31 +8,17 @@ from frappe.model.document import Document
from frappe import _
from frappe.utils import flt
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
class DuplicateDeclarationError(frappe.ValidationError): pass
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionDeclaration(Document):
def validate(self):
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_exemption_amount()
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):
self.total_declared_amount = 0.0
for d in self.declarations:

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext
import unittest
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):
def setUp(self):

View File

@ -21,8 +21,6 @@
"total_actual_amount",
"column_break_12",
"exemption_amount",
"other_incomes_section",
"income_from_other_sources",
"attachment_section",
"attachments",
"amended_from"
@ -111,16 +109,6 @@
"label": "Total Exemption Amount",
"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",
"fieldtype": "Section Break"
@ -142,7 +130,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-03-02 19:02:15.398486",
"modified": "2020-03-18 14:55:51.420016",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Tax Exemption Proof Submission",

View File

@ -7,7 +7,8 @@ import frappe
from frappe.model.document import Document
from frappe import _
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):
def validate(self):
@ -15,6 +16,7 @@ class EmployeeTaxExemptionProofSubmission(Document):
self.set_total_actual_amount()
self.set_total_exemption_amount()
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):
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.monthly_hra_exemption = hra_exemption["monthly_exemption"]
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"]

View File

@ -17,7 +17,7 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
return;
}
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: {
"expense_claim_type": d.expense_type,
"company": doc.company
@ -25,6 +25,7 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
callback: function(r) {
if (r.message) {
d.default_account = r.message.account;
d.cost_center = r.message.cost_center;
}
}
});

View File

@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
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 erpnext.hr.utils import set_employee_name
from erpnext.accounts.party import get_party_account
@ -76,6 +76,7 @@ class ExpenseClaim(AccountsController):
def on_cancel(self):
self.update_task_and_project()
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
if self.payable_account:
self.make_gl_entries(cancel=True)
@ -192,7 +193,8 @@ class ExpenseClaim(AccountsController):
def validate_account_details(self):
for data in self.expenses:
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 not self.mode_of_payment:
@ -259,10 +261,17 @@ class ExpenseClaim(AccountsController):
if not expense.default_account or not validate:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
def update_reimbursed_amount(doc):
amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
def update_reimbursed_amount(doc, jv=None):
condition = ""
if jv:
condition += "and voucher_no = '{0}'".format(jv)
amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) - ifnull(sum(credit_in_account_currency), 0)as amt
from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
and party = %s """, (doc.name, doc.employee) ,as_dict=1)[0].amt
and party = %s {condition}""".format(condition=condition), #nosec
(doc.name, doc.employee) ,as_dict=1)[0].amt
doc.total_amount_reimbursed = amt
frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", amt)
@ -308,13 +317,23 @@ def make_bank_entry(dt, dn):
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()
def get_expense_claim_account(expense_claim_type, company):
account = frappe.db.get_value("Expense Claim Account",
{"parent": expense_claim_type, "company": company}, "default_account")
if not account:
frappe.throw(_("Please set default account in Expense Claim Type {0}")
.format(expense_claim_type))
frappe.throw(_("Set the default account for the {0} {1}")
.format(frappe.bold("Expense Claim Type"), get_link_to_form("Expense Claim Type", expense_claim_type)))
return {
"account": account

View File

@ -13,10 +13,12 @@
"stop_birthday_reminders",
"expense_approver_mandatory_in_expense_claim",
"payroll_settings",
"payroll_based_on",
"max_working_hours_against_timesheet",
"include_holidays_in_total_working_days",
"disable_rounded_total",
"max_working_hours_against_timesheet",
"column_break_11",
"daily_wages_fraction_for_half_day",
"email_salary_slip_to_employee",
"encrypt_salary_slips_in_emails",
"password_policy",
@ -184,13 +186,27 @@
"fieldtype": "Link",
"label": "Role Allowed to Create Backdated Leave Application",
"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",
"idx": 1,
"issingle": 1,
"links": [],
"modified": "2020-01-06 18:46:30.189815",
"modified": "2020-04-13 21:20:59.382394",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",

View File

@ -15,6 +15,9 @@ class HRSettings(Document):
self.set_naming_series()
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):
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
set_by_naming_series("Employee", "employee_number",

View 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', {
});

View File

@ -0,0 +1,152 @@
{
"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,
"collapsible_depends_on": "other_taxes_and_charges",
"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-29 15:08:21.436120",
"modified_by": "Administrator",
"module": "HR",
"name": "Income Tax Slab",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 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
},
{
"amend": 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
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View 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

View 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

View File

@ -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
}

View 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 IncomeTaxSlabOtherCharges(Document):
pass

View File

@ -30,16 +30,16 @@ class LeaveAllocation(Document):
def validate_leave_allocation_days(self):
company = frappe.db.get_value("Employee", self.employee, "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:
leave_allocated = 0
if leave_period:
leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type,
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:
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):
self.create_leave_ledger_entry()

View File

@ -374,7 +374,8 @@ class LeaveApplication(Document):
leaves=self.total_leave_days * -1,
from_date=self.from_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)
@ -384,7 +385,9 @@ class LeaveApplication(Document):
from_date=self.from_date,
to_date=expiry_date,
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)
@ -410,7 +413,7 @@ def get_allocation_expiry(employee, leave_type, to_date, from_date):
return expiry[0]['to_date'] if expiry else None
@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
if cint(half_day) == 1:
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
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
@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'])
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
@ -589,7 +592,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date):
''' Returns leave entries between from_date and to_date. '''
return frappe.db.sql("""
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
FROM `tabLeave Ledger Entry`
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)
@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'''
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
where h1.parent = h2.name and h1.holiday_date between %s and %s

View File

@ -1,4 +1,5 @@
{
"actions": [],
"creation": "2019-05-09 15:47:39.760406",
"doctype": "DocType",
"engine": "InnoDB",
@ -12,6 +13,7 @@
"column_break_7",
"from_date",
"to_date",
"holiday_list",
"is_carry_forward",
"is_expired",
"is_lwp",
@ -98,11 +100,18 @@
"fieldname": "is_lwp",
"fieldtype": "Check",
"label": "Is Leave Without Pay"
},
{
"fieldname": "holiday_list",
"fieldtype": "Link",
"label": "Holiday List",
"options": "Holiday List"
}
],
"in_create": 1,
"is_submittable": 1,
"modified": "2019-08-20 14:40:04.130799",
"links": [],
"modified": "2020-02-27 14:40:10.502605",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Ledger Entry",

View File

@ -1,401 +1,102 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "Prompt",
"beta": 0,
"creation": "2018-04-13 15:18:53.698553",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_import": 1,
"autoname": "Prompt",
"creation": "2018-04-13 15:18:53.698553",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"column_break_2",
"start_date",
"end_date",
"section_break_5",
"periods"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"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
},
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
},
"fieldname": "start_date",
"fieldtype": "Date",
"label": "Start Date",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
},
"fieldname": "end_date",
"fieldtype": "Date",
"label": "End Date",
"reqd": 1
},
{
"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_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
},
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 1,
"label": "Payroll Periods"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
"fieldname": "periods",
"fieldtype": "Table",
"label": "Payroll Periods",
"options": "Payroll Period Date"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"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",
],
"links": [],
"modified": "2020-03-18 18:13:23.859980",
"modified_by": "Administrator",
"module": "HR",
"name": "Payroll Period",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -45,8 +45,9 @@ class PayrollPeriod(Document):
+ _(") for {0}").format(self.company)
frappe.throw(msg)
def get_payroll_period_days(start_date, end_date, employee):
company = frappe.db.get_value("Employee", employee, "company")
def get_payroll_period_days(start_date, end_date, employee, company=None):
if not company:
company = frappe.db.get_value("Employee", employee, "company")
payroll_period = frappe.db.sql("""
select name, start_date, end_date
from `tabPayroll Period`

View File

@ -1,264 +1,263 @@
{
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:salary_component",
"creation": "2016-06-30 15:42:43.631931",
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"salary_component",
"salary_component_abbr",
"type",
"description",
"column_break_4",
"is_payable",
"depends_on_payment_days",
"is_tax_applicable",
"deduct_full_tax_on_selected_payroll_date",
"round_to_the_nearest_integer",
"statistical_component",
"do_not_include_in_total",
"disabled",
"flexible_benefits",
"is_flexible_benefit",
"max_benefit_amount",
"column_break_9",
"pay_against_benefit_claim",
"only_tax_impact",
"create_separate_payment_entry_against_benefit_claim",
"section_break_11",
"variable_based_on_taxable_salary",
"section_break_5",
"accounts",
"condition_and_formula",
"condition",
"amount",
"amount_based_on_formula",
"formula",
"column_break_28",
"help"
],
"fields": [
{
"fieldname": "salary_component",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "salary_component_abbr",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Abbr",
"print_width": "120px",
"reqd": 1,
"width": "120px"
},
{
"fieldname": "type",
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Type",
"options": "Earning\nDeduction",
"reqd": 1
},
{
"default": "1",
"depends_on": "eval:doc.type == \"Earning\"",
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
"label": "Is Tax Applicable"
},
{
"default": "1",
"fieldname": "is_payable",
"fieldtype": "Check",
"label": "Is Payable"
},
{
"default": "1",
"fieldname": "depends_on_payment_days",
"fieldtype": "Check",
"label": "Depends on Payment Days",
"print_hide": 1
},
{
"default": "0",
"fieldname": "do_not_include_in_total",
"fieldtype": "Check",
"label": "Do Not Include in Total"
},
{
"default": "0",
"depends_on": "is_tax_applicable",
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"label": "Deduct Full Tax on Selected Payroll Date"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Description"
},
{
"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. ",
"fieldname": "statistical_component",
"fieldtype": "Check",
"label": "Statistical Component"
},
{
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
"fieldname": "flexible_benefits",
"fieldtype": "Section Break",
"label": "Flexible Benefits"
},
{
"default": "0",
"fieldname": "is_flexible_benefit",
"fieldtype": "Check",
"label": "Is Flexible Benefit"
},
{
"depends_on": "is_flexible_benefit",
"fieldname": "max_benefit_amount",
"fieldtype": "Currency",
"label": "Max Benefit Amount (Yearly)"
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"default": "0",
"depends_on": "is_flexible_benefit",
"fieldname": "pay_against_benefit_claim",
"fieldtype": "Check",
"label": "Pay Against Benefit Claim"
},
{
"default": "0",
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1",
"fieldname": "only_tax_impact",
"fieldtype": "Check",
"label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)"
},
{
"default": "0",
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1",
"fieldname": "create_separate_payment_entry_against_benefit_claim",
"fieldtype": "Check",
"label": "Create Separate Payment Entry Against Benefit Claim"
},
{
"depends_on": "eval:doc.type=='Deduction'",
"fieldname": "section_break_11",
"fieldtype": "Section Break"
},
{
"default": "0",
"fieldname": "variable_based_on_taxable_salary",
"fieldtype": "Check",
"label": "Variable Based On Taxable Salary"
},
{
"depends_on": "eval:doc.statistical_component != 1",
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"label": "Accounts"
},
{
"fieldname": "accounts",
"fieldtype": "Table",
"label": "Accounts",
"options": "Salary Component Account"
},
{
"collapsible": 1,
"depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
"fieldname": "condition_and_formula",
"fieldtype": "Section Break",
"label": "Condition and Formula"
},
{
"fieldname": "condition",
"fieldtype": "Code",
"label": "Condition"
},
{
"default": "0",
"fieldname": "amount_based_on_formula",
"fieldtype": "Check",
"label": "Amount based on formula"
},
{
"depends_on": "amount_based_on_formula",
"fieldname": "formula",
"fieldtype": "Code",
"label": "Formula"
},
{
"depends_on": "eval:doc.amount_based_on_formula!==1",
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount"
},
{
"fieldname": "column_break_28",
"fieldtype": "Column Break"
},
{
"fieldname": "help",
"fieldtype": "HTML",
"label": "Help",
"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 &lt; 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 &gt; 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",
"fieldname": "round_to_the_nearest_integer",
"fieldtype": "Check",
"label": "Round to the Nearest Integer"
}
],
"icon": "fa fa-flag",
"modified": "2019-06-05 11:34:14.231228",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"write": 1
},
{
"read": 1,
"role": "Employee"
}
],
"sort_field": "modified",
"sort_order": "DESC"
}
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:salary_component",
"creation": "2016-06-30 15:42:43.631931",
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"salary_component",
"salary_component_abbr",
"type",
"description",
"column_break_4",
"depends_on_payment_days",
"is_tax_applicable",
"deduct_full_tax_on_selected_payroll_date",
"variable_based_on_taxable_salary",
"exempted_from_income_tax",
"round_to_the_nearest_integer",
"statistical_component",
"do_not_include_in_total",
"disabled",
"flexible_benefits",
"is_flexible_benefit",
"max_benefit_amount",
"column_break_9",
"pay_against_benefit_claim",
"only_tax_impact",
"create_separate_payment_entry_against_benefit_claim",
"section_break_5",
"accounts",
"condition_and_formula",
"condition",
"amount",
"amount_based_on_formula",
"formula",
"column_break_28",
"help"
],
"fields": [
{
"fieldname": "salary_component",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "salary_component_abbr",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Abbr",
"print_width": "120px",
"reqd": 1,
"width": "120px"
},
{
"fieldname": "type",
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Type",
"options": "Earning\nDeduction",
"reqd": 1
},
{
"default": "1",
"depends_on": "eval:doc.type == \"Earning\"",
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
"label": "Is Tax Applicable"
},
{
"default": "1",
"fieldname": "depends_on_payment_days",
"fieldtype": "Check",
"label": "Depends on Payment Days",
"print_hide": 1
},
{
"default": "0",
"fieldname": "do_not_include_in_total",
"fieldtype": "Check",
"label": "Do Not Include in Total"
},
{
"default": "0",
"depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'",
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"label": "Deduct Full Tax on Selected Payroll Date"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Description"
},
{
"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. ",
"fieldname": "statistical_component",
"fieldtype": "Check",
"label": "Statistical Component"
},
{
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
"fieldname": "flexible_benefits",
"fieldtype": "Section Break",
"label": "Flexible Benefits"
},
{
"default": "0",
"fieldname": "is_flexible_benefit",
"fieldtype": "Check",
"label": "Is Flexible Benefit"
},
{
"depends_on": "is_flexible_benefit",
"fieldname": "max_benefit_amount",
"fieldtype": "Currency",
"label": "Max Benefit Amount (Yearly)"
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"default": "0",
"depends_on": "is_flexible_benefit",
"fieldname": "pay_against_benefit_claim",
"fieldtype": "Check",
"label": "Pay Against Benefit Claim"
},
{
"default": "0",
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1",
"fieldname": "only_tax_impact",
"fieldtype": "Check",
"label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)"
},
{
"default": "0",
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1",
"fieldname": "create_separate_payment_entry_against_benefit_claim",
"fieldtype": "Check",
"label": "Create Separate Payment Entry Against Benefit Claim"
},
{
"default": "0",
"depends_on": "eval:doc.type == \"Deduction\"",
"fieldname": "variable_based_on_taxable_salary",
"fieldtype": "Check",
"label": "Variable Based On Taxable Salary"
},
{
"depends_on": "eval:doc.statistical_component != 1",
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"label": "Accounts"
},
{
"fieldname": "accounts",
"fieldtype": "Table",
"label": "Accounts",
"options": "Salary Component Account"
},
{
"collapsible": 1,
"depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
"fieldname": "condition_and_formula",
"fieldtype": "Section Break",
"label": "Condition and Formula"
},
{
"fieldname": "condition",
"fieldtype": "Code",
"label": "Condition"
},
{
"default": "0",
"fieldname": "amount_based_on_formula",
"fieldtype": "Check",
"label": "Amount based on formula"
},
{
"depends_on": "amount_based_on_formula",
"fieldname": "formula",
"fieldtype": "Code",
"label": "Formula"
},
{
"depends_on": "eval:doc.amount_based_on_formula!==1",
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount"
},
{
"fieldname": "column_break_28",
"fieldtype": "Column Break"
},
{
"fieldname": "help",
"fieldtype": "HTML",
"label": "Help",
"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 &lt; 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 &gt; 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",
"fieldname": "round_to_the_nearest_integer",
"fieldtype": "Check",
"label": "Round to the Nearest Integer"
},
{
"default": "0",
"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 without any declaration or proof submission.",
"fieldname": "exempted_from_income_tax",
"fieldtype": "Check",
"label": "Exempted from Income Tax"
}
],
"icon": "fa fa-flag",
"links": [],
"modified": "2020-04-28 15:46:45.252945",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"write": 1
},
{
"read": 1,
"role": "Employee"
}
],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -3,14 +3,12 @@
"doctype": "Salary Component",
"salary_component": "_Test Basic Salary",
"type": "Earning",
"is_payable": 1,
"is_tax_applicable": 1
},
{
"doctype": "Salary Component",
"salary_component": "_Test Allowance",
"type": "Earning",
"is_payable": 1,
"is_tax_applicable": 1
},
{
@ -27,14 +25,12 @@
"doctype": "Salary Component",
"salary_component": "Basic",
"type": "Earning",
"is_payable": 1,
"is_tax_applicable": 1
},
{
"doctype": "Salary Component",
"salary_component": "Leave Encashment",
"type": "Earning",
"is_payable": 1,
"is_tax_applicable": 1
}
]

View File

@ -18,6 +18,5 @@ def create_salary_component(component_name, **args):
"doctype": "Salary Component",
"salary_component": component_name,
"type": args.get("type") or "Earning",
"is_payable": args.get("is_payable") or 1,
"is_tax_applicable": args.get("is_tax_applicable") or 1
}).insert()

View File

@ -12,6 +12,7 @@
"deduct_full_tax_on_selected_payroll_date",
"depends_on_payment_days",
"is_tax_applicable",
"exempted_from_income_tax",
"is_flexible_benefit",
"variable_based_on_taxable_salary",
"section_break_2",
@ -62,6 +63,7 @@
},
{
"default": "0",
"depends_on": "eval:doc.parentfield=='earnings'",
"fetch_from": "salary_component.is_tax_applicable",
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
@ -71,6 +73,7 @@
},
{
"default": "0",
"depends_on": "eval:doc.parentfield=='earnings'",
"fetch_from": "salary_component.is_flexible_benefit",
"fieldname": "is_flexible_benefit",
"fieldtype": "Check",
@ -80,6 +83,7 @@
},
{
"default": "0",
"depends_on": "eval:doc.parentfield=='deductions'",
"fetch_from": "salary_component.variable_based_on_taxable_salary",
"fieldname": "variable_based_on_taxable_salary",
"fieldtype": "Check",
@ -187,11 +191,20 @@
"fieldtype": "HTML",
"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 &lt; 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 &gt; 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,
"links": [],
"modified": "2019-12-31 17:15:25.646689",
"modified": "2020-04-24 20:00:16.475295",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Detail",

View File

@ -51,7 +51,7 @@ frappe.ui.form.on("Salary Slip", {
},
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){
@ -86,7 +86,7 @@ frappe.ui.form.on("Salary Slip", {
salary_slip_based_on_timesheet: function(frm) {
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) {
@ -95,15 +95,14 @@ frappe.ui.form.on("Salary Slip", {
},
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){
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
return frappe.call({
method: 'process_salary_based_on_leave',
method: 'process_salary_based_on_working_days',
doc: frm.doc,
args: {"lwp": frm.doc.leave_without_pay},
callback: function(r, rt) {
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(['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({
method: 'get_emp_and_leave_details',
method: 'get_emp_and_working_day_details',
doc: frm.doc,
callback: function(r, rt) {
frm.refresh();

Some files were not shown because too many files have changed in this diff Show More