Merge branch 'develop' into patch-8
This commit is contained in:
commit
758f0f0780
@ -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}))
|
||||
{"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}))
|
||||
{"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:
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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()
|
||||
|
||||
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
||||
if not from_repost:
|
||||
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
|
||||
self.validate_account_details(adv_adj)
|
||||
self.validate_dimensions_for_pl_and_bs()
|
||||
check_freezing_date(self.posting_date, adv_adj)
|
||||
|
||||
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}"\
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
})
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ 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,
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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);
|
||||
}
|
||||
})
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -138,7 +138,6 @@
|
||||
"row_id": 7
|
||||
}
|
||||
],
|
||||
"posting_date": "2013-02-03",
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier"
|
||||
},
|
||||
@ -204,7 +203,6 @@
|
||||
"tax_amount": 150.0
|
||||
}
|
||||
],
|
||||
"posting_date": "2013-02-03",
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier"
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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
|
||||
@ -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:
|
||||
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)
|
||||
|
||||
if not from_repost:
|
||||
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,
|
||||
def make_reverse_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
|
||||
"""
|
||||
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:
|
||||
validate_frozen_account(entry["account"], adv_adj)
|
||||
validate_balance_type(entry["account"], adv_adj)
|
||||
if not adv_adj:
|
||||
validate_expense_against_budget(entry)
|
||||
entry['name'] = None
|
||||
debit = entry.get('debit', 0)
|
||||
credit = entry.get('credit', 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)
|
||||
debit_in_account_currency = entry.get('debit_in_account_currency', 0)
|
||||
credit_in_account_currency = entry.get('credit_in_account_currency', 0)
|
||||
|
||||
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))
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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") + "'"
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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 '-' '''
|
||||
@ -898,3 +887,59 @@ def get_stock_accounts(company):
|
||||
"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
|
||||
@ -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):
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
}
|
||||
@ -23,11 +23,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Payments'),
|
||||
'items': ['Payment Entry']
|
||||
},
|
||||
{
|
||||
'label': _('Bank'),
|
||||
'items': ['Bank Account']
|
||||
'items': ['Payment Entry', 'Bank Account']
|
||||
},
|
||||
{
|
||||
'label': _('Pricing'),
|
||||
|
||||
@ -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
|
||||
@ -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",
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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\"}",
|
||||
|
||||
@ -16,16 +16,19 @@ 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;
|
||||
}
|
||||
|
||||
if (flag){
|
||||
frm.dashboard.set_headline_alert(
|
||||
`<div class="row">
|
||||
<div class="col-xs-12">
|
||||
@ -33,6 +36,7 @@ frappe.ui.form.on('Twitter Settings', {
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
},
|
||||
login: function(frm){
|
||||
if (frm.doc.consumer_key && frm.doc.consumer_secret){
|
||||
|
||||
24
erpnext/crm/utils.py
Normal file
24
erpnext/crm/utils.py
Normal 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()
|
||||
@ -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) {
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
|
||||
@ -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']
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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):
|
||||
@ -53,3 +72,61 @@ def create_therapy_session(source_name, target_doc=None):
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
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)
|
||||
@ -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
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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'),
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Employee Other Income', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
@ -0,0 +1,138 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "HR-INCOME-.######",
|
||||
"creation": "2020-03-18 15:04:40.767434",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"employee",
|
||||
"employee_name",
|
||||
"payroll_period",
|
||||
"column_break_3",
|
||||
"company",
|
||||
"source",
|
||||
"amount",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Employee",
|
||||
"options": "Employee",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "payroll_period",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Payroll Period",
|
||||
"options": "Payroll Period",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "source",
|
||||
"fieldtype": "Data",
|
||||
"label": "Source"
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Employee Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Employee Other Income",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-19 18:06:45.361830",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Other Income",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class EmployeeOtherIncome(Document):
|
||||
pass
|
||||
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestEmployeeOtherIncome(unittest.TestCase):
|
||||
pass
|
||||
@ -1,534 +1,116 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"actions": [],
|
||||
"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",
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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",
|
||||
"links": [],
|
||||
"modified": "2020-03-18 14:56:25.625717",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Tax Exemption Declaration",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@ -538,14 +120,10 @@
|
||||
"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,
|
||||
"write": 1
|
||||
@ -557,14 +135,10 @@
|
||||
"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,
|
||||
"write": 1
|
||||
@ -576,14 +150,10 @@
|
||||
"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,
|
||||
"write": 1
|
||||
@ -595,26 +165,16 @@
|
||||
"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,
|
||||
"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
|
||||
"track_changes": 1
|
||||
}
|
||||
@ -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:
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
0
erpnext/hr/doctype/income_tax_slab/__init__.py
Normal file
0
erpnext/hr/doctype/income_tax_slab/__init__.py
Normal file
6
erpnext/hr/doctype/income_tax_slab/income_tax_slab.js
Normal file
6
erpnext/hr/doctype/income_tax_slab/income_tax_slab.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Income Tax Slab', {
|
||||
|
||||
});
|
||||
152
erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
Normal file
152
erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
Normal 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
|
||||
}
|
||||
10
erpnext/hr/doctype/income_tax_slab/income_tax_slab.py
Normal file
10
erpnext/hr/doctype/income_tax_slab/income_tax_slab.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class IncomeTaxSlab(Document):
|
||||
pass
|
||||
10
erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py
Normal file
10
erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestIncomeTaxSlab(unittest.TestCase):
|
||||
pass
|
||||
@ -0,0 +1,75 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-04-24 11:46:59.041180",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"description",
|
||||
"column_break_2",
|
||||
"percent",
|
||||
"conditions_section",
|
||||
"min_taxable_income",
|
||||
"column_break_7",
|
||||
"max_taxable_income"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "min_taxable_income",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Min Taxable Income",
|
||||
"options": "Company:company:default_currency"
|
||||
},
|
||||
{
|
||||
"columns": 4,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "percent",
|
||||
"fieldtype": "Percent",
|
||||
"in_list_view": 1,
|
||||
"label": "Percent",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "conditions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Conditions"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "max_taxable_income",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Max Taxable Income",
|
||||
"options": "Company:company:default_currency"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-24 13:27:43.598967",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Income Tax Slab Other Charges",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class IncomeTaxSlabOtherCharges(Document):
|
||||
pass
|
||||
@ -30,13 +30,13 @@ 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))
|
||||
|
||||
@ -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,8 +610,9 @@ 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'''
|
||||
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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1,401 +1,102 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"actions": [],
|
||||
"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",
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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
|
||||
"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",
|
||||
"links": [],
|
||||
"modified": "2020-03-18 18:13:23.859980",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Payroll Period",
|
||||
"name_case": "",
|
||||
"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,
|
||||
"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,
|
||||
"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,
|
||||
"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
|
||||
"track_changes": 1
|
||||
}
|
||||
@ -45,7 +45,8 @@ class PayrollPeriod(Document):
|
||||
+ _(") for {0}").format(self.company)
|
||||
frappe.throw(msg)
|
||||
|
||||
def get_payroll_period_days(start_date, end_date, employee):
|
||||
def get_payroll_period_days(start_date, end_date, employee, company=None):
|
||||
if not company:
|
||||
company = frappe.db.get_value("Employee", employee, "company")
|
||||
payroll_period = frappe.db.sql("""
|
||||
select name, start_date, end_date
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:salary_component",
|
||||
@ -13,10 +14,11 @@
|
||||
"type",
|
||||
"description",
|
||||
"column_break_4",
|
||||
"is_payable",
|
||||
"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",
|
||||
@ -28,8 +30,6 @@
|
||||
"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",
|
||||
@ -73,12 +73,6 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Tax Applicable"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "is_payable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Payable"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "depends_on_payment_days",
|
||||
@ -94,7 +88,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "is_tax_applicable",
|
||||
"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"
|
||||
@ -165,13 +159,9 @@
|
||||
"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",
|
||||
"depends_on": "eval:doc.type == \"Deduction\"",
|
||||
"fieldname": "variable_based_on_taxable_salary",
|
||||
"fieldtype": "Check",
|
||||
"label": "Variable Based On Taxable Salary"
|
||||
@ -233,10 +223,19 @@
|
||||
"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",
|
||||
"modified": "2019-06-05 11:34:14.231228",
|
||||
"links": [],
|
||||
"modified": "2020-04-28 15:46:45.252945",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Component",
|
||||
@ -261,4 +260,4 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
]
|
||||
@ -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()
|
||||
|
||||
@ -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 < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.parentfield=='deductions'",
|
||||
"fetch_from": "salary_component.exempted_from_income_tax",
|
||||
"fieldname": "exempted_from_income_tax",
|
||||
"fieldtype": "Check",
|
||||
"label": "Exempted from Income Tax",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"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",
|
||||
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user