mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-23 15:09:20 +00:00
Merge branch 'v12-pre-release' into version-12
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '12.8.0'
|
__version__ = '12.9.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ def book_deferred_income_or_expense(doc, posting_date=None):
|
|||||||
total_days, total_booking_days, account_currency)
|
total_days, total_booking_days, account_currency)
|
||||||
|
|
||||||
make_gl_entries(doc, credit_account, debit_account, against,
|
make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
amount, base_amount, end_date, project, account_currency, item.cost_center, item.name)
|
amount, base_amount, end_date, project, account_currency, item.cost_center, item)
|
||||||
|
|
||||||
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
||||||
_book_deferred_revenue_or_expense(item)
|
_book_deferred_revenue_or_expense(item)
|
||||||
@@ -170,7 +170,7 @@ def book_deferred_income_or_expense(doc, posting_date=None):
|
|||||||
_book_deferred_revenue_or_expense(item)
|
_book_deferred_revenue_or_expense(item)
|
||||||
|
|
||||||
def make_gl_entries(doc, credit_account, debit_account, against,
|
def make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no):
|
amount, base_amount, posting_date, project, account_currency, cost_center, item):
|
||||||
# GL Entry for crediting the amount in the deferred expense
|
# GL Entry for crediting the amount in the deferred expense
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
@@ -184,10 +184,10 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
|||||||
"credit": base_amount,
|
"credit": base_amount,
|
||||||
"credit_in_account_currency": amount,
|
"credit_in_account_currency": amount,
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
"voucher_detail_no": voucher_detail_no,
|
"voucher_detail_no": item.name,
|
||||||
'posting_date': posting_date,
|
'posting_date': posting_date,
|
||||||
'project': project
|
'project': project
|
||||||
}, account_currency)
|
}, account_currency, item=item)
|
||||||
)
|
)
|
||||||
# GL Entry to debit the amount from the expense
|
# GL Entry to debit the amount from the expense
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -197,10 +197,10 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
|||||||
"debit": base_amount,
|
"debit": base_amount,
|
||||||
"debit_in_account_currency": amount,
|
"debit_in_account_currency": amount,
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
"voucher_detail_no": voucher_detail_no,
|
"voucher_detail_no": item.name,
|
||||||
'posting_date': posting_date,
|
'posting_date': posting_date,
|
||||||
'project': project
|
'project': project
|
||||||
}, account_currency)
|
}, account_currency, item=item)
|
||||||
)
|
)
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class Account(NestedSet):
|
|||||||
throw(_("Root cannot be edited."), RootNotEditable)
|
throw(_("Root cannot be edited."), RootNotEditable)
|
||||||
|
|
||||||
if not self.parent_account and not self.is_group:
|
if not self.parent_account and not self.is_group:
|
||||||
frappe.throw(_("Root Account must be a group"))
|
frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
|
||||||
|
|
||||||
def validate_root_company_and_sync_account_to_children(self):
|
def validate_root_company_and_sync_account_to_children(self):
|
||||||
# ignore validation while creating new compnay or while syncing to child companies
|
# ignore validation while creating new compnay or while syncing to child companies
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class TestAccount(unittest.TestCase):
|
|||||||
acc.account_name = "Accumulated Depreciation"
|
acc.account_name = "Accumulated Depreciation"
|
||||||
acc.parent_account = "Fixed Assets - _TC"
|
acc.parent_account = "Fixed Assets - _TC"
|
||||||
acc.company = "_Test Company"
|
acc.company = "_Test Company"
|
||||||
|
acc.account_type = "Accumulated Depreciation"
|
||||||
acc.insert()
|
acc.insert()
|
||||||
|
|
||||||
doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
|
doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
|
||||||
@@ -149,7 +150,7 @@ def _make_test_records(verbose):
|
|||||||
|
|
||||||
# fixed asset depreciation
|
# fixed asset depreciation
|
||||||
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
||||||
["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
|
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
|
||||||
["_Test Depreciations", "Expenses", 0, None, None],
|
["_Test Depreciations", "Expenses", 0, None, None],
|
||||||
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
||||||
|
|
||||||
|
|||||||
@@ -162,9 +162,9 @@ def toggle_disabling(doc):
|
|||||||
|
|
||||||
def get_doctypes_with_dimensions():
|
def get_doctypes_with_dimensions():
|
||||||
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||||
"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item",
|
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
|
||||||
"Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item",
|
"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||||
"Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||||
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
||||||
"Subscription Plan"]
|
"Subscription Plan"]
|
||||||
@@ -206,12 +206,13 @@ def get_dimension_filters():
|
|||||||
WHERE disabled = 0
|
WHERE disabled = 0
|
||||||
""", as_dict=1)
|
""", as_dict=1)
|
||||||
|
|
||||||
default_dimensions = frappe.db.sql("""SELECT parent, company, default_dimension
|
default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
|
||||||
FROM `tabAccounting Dimension Detail`""", as_dict=1)
|
FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
|
||||||
|
WHERE c.parent = p.name""", as_dict=1)
|
||||||
|
|
||||||
default_dimensions_map = {}
|
default_dimensions_map = {}
|
||||||
for dimension in default_dimensions:
|
for dimension in default_dimensions:
|
||||||
default_dimensions_map.setdefault(dimension['company'], {})
|
default_dimensions_map.setdefault(dimension.company, {})
|
||||||
default_dimensions_map[dimension['company']][dimension['parent']] = dimension['default_dimension']
|
default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension
|
||||||
|
|
||||||
return dimension_filters, default_dimensions_map
|
return dimension_filters, default_dimensions_map
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowd
|
|||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class BudgetError(frappe.ValidationError): pass
|
class BudgetError(frappe.ValidationError): pass
|
||||||
class DuplicateBudgetError(frappe.ValidationError): pass
|
class DuplicateBudgetError(frappe.ValidationError): pass
|
||||||
@@ -98,30 +99,32 @@ def validate_expense_against_budget(args):
|
|||||||
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
||||||
args.cost_center, args.account = get_item_details(args)
|
args.cost_center, args.account = get_item_details(args)
|
||||||
|
|
||||||
if not (args.cost_center or args.project) and not args.account:
|
if not args.account:
|
||||||
return
|
return
|
||||||
|
|
||||||
for budget_against in ['project', 'cost_center']:
|
for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
|
||||||
if (args.get(budget_against) and args.account
|
if (args.get(budget_against) and args.account
|
||||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
||||||
|
|
||||||
if args.project and budget_against == 'project':
|
doctype = frappe.unscrub(budget_against)
|
||||||
condition = "and b.project=%s" % frappe.db.escape(args.project)
|
|
||||||
args.budget_against_field = "Project"
|
|
||||||
|
|
||||||
elif args.cost_center and budget_against == 'cost_center':
|
if frappe.get_cached_value('DocType', doctype, 'is_tree'):
|
||||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
|
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||||
condition = """and exists(select name from `tabCost Center`
|
condition = """and exists(select name from `tab%s`
|
||||||
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt)
|
where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
|
||||||
args.budget_against_field = "Cost Center"
|
args.is_tree = True
|
||||||
|
else:
|
||||||
|
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
||||||
|
args.is_tree = False
|
||||||
|
|
||||||
args.budget_against = args.get(budget_against)
|
args.budget_against_field = budget_against
|
||||||
|
args.budget_against_doctype = doctype
|
||||||
|
|
||||||
budget_records = frappe.db.sql("""
|
budget_records = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||||
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
||||||
ifnull(applicable_on_purchase_order,0) as for_purchase_order,
|
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||||
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
|
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
|
||||||
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
|
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
|
||||||
@@ -132,9 +135,7 @@ def validate_expense_against_budget(args):
|
|||||||
b.name=ba.parent and b.fiscal_year=%s
|
b.name=ba.parent and b.fiscal_year=%s
|
||||||
and ba.account=%s and b.docstatus=1
|
and ba.account=%s and b.docstatus=1
|
||||||
{condition}
|
{condition}
|
||||||
""".format(condition=condition,
|
""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
|
||||||
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
|
|
||||||
(args.fiscal_year, args.account), as_dict=True)
|
|
||||||
|
|
||||||
if budget_records:
|
if budget_records:
|
||||||
validate_budget_records(args, budget_records)
|
validate_budget_records(args, budget_records)
|
||||||
@@ -230,10 +231,10 @@ def get_ordered_amount(args, budget):
|
|||||||
|
|
||||||
def get_other_condition(args, budget, for_doc):
|
def get_other_condition(args, budget, for_doc):
|
||||||
condition = "expense_account = '%s'" % (args.expense_account)
|
condition = "expense_account = '%s'" % (args.expense_account)
|
||||||
budget_against_field = frappe.scrub(args.get("budget_against_field"))
|
budget_against_field = args.get("budget_against_field")
|
||||||
|
|
||||||
if budget_against_field and args.get(budget_against_field):
|
if budget_against_field and args.get(budget_against_field):
|
||||||
condition += " and child.%s = '%s'" %(budget_against_field, args.get(budget_against_field))
|
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
||||||
|
|
||||||
if args.get('fiscal_year'):
|
if args.get('fiscal_year'):
|
||||||
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
|
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
|
||||||
@@ -246,19 +247,30 @@ def get_other_condition(args, budget, for_doc):
|
|||||||
return condition
|
return condition
|
||||||
|
|
||||||
def get_actual_expense(args):
|
def get_actual_expense(args):
|
||||||
|
if not args.budget_against_doctype:
|
||||||
|
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
|
||||||
|
|
||||||
|
budget_against_field = args.get('budget_against_field')
|
||||||
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
||||||
if args.get("month_end_date") else ""
|
if args.get("month_end_date") else ""
|
||||||
if args.budget_against_field == "Cost Center":
|
|
||||||
lft_rgt = frappe.db.get_value(args.budget_against_field,
|
if args.is_tree:
|
||||||
args.budget_against, ["lft", "rgt"], as_dict=1)
|
lft_rgt = frappe.db.get_value(args.budget_against_doctype,
|
||||||
|
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
|
||||||
|
|
||||||
args.update(lft_rgt)
|
args.update(lft_rgt)
|
||||||
condition2 = """and exists(select name from `tabCost Center`
|
|
||||||
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
|
|
||||||
|
|
||||||
elif args.budget_against_field == "Project":
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)"
|
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||||
|
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
|
||||||
|
budget_against_field=budget_against_field)
|
||||||
|
else:
|
||||||
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
|
where name=gle.{budget_against} and
|
||||||
|
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
|
||||||
|
budget_against = budget_against_field)
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
amount = flt(frappe.db.sql("""
|
||||||
select sum(gle.debit) - sum(gle.credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
where gle.account=%(account)s
|
where gle.account=%(account)s
|
||||||
@@ -267,7 +279,9 @@ def get_actual_expense(args):
|
|||||||
and gle.company=%(company)s
|
and gle.company=%(company)s
|
||||||
and gle.docstatus=1
|
and gle.docstatus=1
|
||||||
{condition2}
|
{condition2}
|
||||||
""".format(condition1=condition1, condition2=condition2), (args))[0][0])
|
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
|
||||||
|
|
||||||
|
return amount
|
||||||
|
|
||||||
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
||||||
distribution = {}
|
distribution = {}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
|
|||||||
|
|
||||||
class TestBudget(unittest.TestCase):
|
class TestBudget(unittest.TestCase):
|
||||||
def test_monthly_budget_crossed_ignore(self):
|
def test_monthly_budget_crossed_ignore(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop1(self):
|
def test_monthly_budget_crossed_stop1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_exception_approver_role(self):
|
def test_exception_approver_role(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop2(self):
|
def test_monthly_budget_crossed_stop2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop1(self):
|
def test_yearly_budget_crossed_stop1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop2(self):
|
def test_yearly_budget_crossed_stop2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation1(self):
|
def test_monthly_budget_on_cancellation1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation2(self):
|
def test_monthly_budget_on_cancellation2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@@ -201,8 +201,8 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_against_group_cost_center(self):
|
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")
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC")
|
set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _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")
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
@@ -241,25 +241,30 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||||
if budget_against_field == "Project":
|
if budget_against_field == "project":
|
||||||
budget_against = "_Test Project"
|
budget_against = "_Test Project"
|
||||||
else:
|
else:
|
||||||
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
||||||
existing_expense = get_actual_expense(frappe._dict({
|
|
||||||
|
args = frappe._dict({
|
||||||
"account": "_Test Account Cost for Goods Sold - _TC",
|
"account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"monthly_end_date": posting_date,
|
"monthly_end_date": posting_date,
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
"fiscal_year": "_Test Fiscal Year 2013",
|
||||||
"budget_against_field": budget_against_field,
|
"budget_against_field": budget_against_field,
|
||||||
"budget_against": budget_against
|
})
|
||||||
}))
|
|
||||||
|
if not args.get(budget_against_field):
|
||||||
|
args[budget_against_field] = budget_against
|
||||||
|
|
||||||
|
existing_expense = get_actual_expense(args)
|
||||||
|
|
||||||
if existing_expense:
|
if existing_expense:
|
||||||
if budget_against_field == "Cost Center":
|
if budget_against_field == "cost_center":
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
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="2013-02-28", submit=True)
|
||||||
elif budget_against_field == "Project":
|
elif budget_against_field == "project":
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
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="2013-02-28")
|
||||||
|
|
||||||
|
|||||||
@@ -17,17 +17,60 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
if (frm.page && frm.page.show_import_button) {
|
if (frm.page && frm.page.show_import_button) {
|
||||||
create_import_button(frm);
|
create_import_button(frm);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// show download template button when company is properly selected
|
download_template: function(frm) {
|
||||||
if(frm.doc.company) {
|
var d = new frappe.ui.Dialog({
|
||||||
// download the csv template file
|
title: __("Download Template"),
|
||||||
frm.add_custom_button(__("Download template"), function () {
|
fields: [
|
||||||
let get_template_url = 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template';
|
{
|
||||||
open_url_post(frappe.request.url, { cmd: get_template_url, doctype: frm.doc.doctype });
|
label : "File Type",
|
||||||
});
|
fieldname: "file_type",
|
||||||
} else {
|
fieldtype: "Select",
|
||||||
frm.set_value("import_file", "");
|
reqd: 1,
|
||||||
}
|
options: ["Excel", "CSV"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Template Type",
|
||||||
|
fieldname: "template_type",
|
||||||
|
fieldtype: "Select",
|
||||||
|
reqd: 1,
|
||||||
|
options: ["Sample Template", "Blank Template"],
|
||||||
|
change: () => {
|
||||||
|
let template_type = d.get_value('template_type');
|
||||||
|
|
||||||
|
if (template_type === "Sample Template") {
|
||||||
|
d.set_df_property('template_type', 'description',
|
||||||
|
`The Sample Template contains all the required accounts pre filled in the template.
|
||||||
|
You can add more accounts or change existing accounts in the template as per your choice.`);
|
||||||
|
} else {
|
||||||
|
d.set_df_property('template_type', 'description',
|
||||||
|
`The Blank Template contains just the account type and root type required to build the Chart
|
||||||
|
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
primary_action: function() {
|
||||||
|
var data = d.get_values();
|
||||||
|
|
||||||
|
if (!data.template_type) {
|
||||||
|
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
||||||
|
}
|
||||||
|
|
||||||
|
open_url_post(
|
||||||
|
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
||||||
|
{
|
||||||
|
file_type: data.file_type,
|
||||||
|
template_type: data.template_type
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
d.hide();
|
||||||
|
},
|
||||||
|
primary_action_label: __('Download')
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
import_file: function (frm) {
|
import_file: function (frm) {
|
||||||
@@ -41,21 +84,24 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
company: function (frm) {
|
company: function (frm) {
|
||||||
// validate that no Gl Entry record for the company exists.
|
if (frm.doc.company) {
|
||||||
frappe.call({
|
// validate that no Gl Entry record for the company exists.
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
frappe.call({
|
||||||
args: {
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
||||||
company: frm.doc.company
|
args: {
|
||||||
},
|
company: frm.doc.company
|
||||||
callback: function(r) {
|
},
|
||||||
if(r.message===false) {
|
callback: function(r) {
|
||||||
frm.set_value("company", "");
|
if(r.message===false) {
|
||||||
frappe.throw(__("Transactions against the company already exist! "));
|
frm.set_value("company", "");
|
||||||
} else {
|
frappe.throw(__(`Transactions against the company already exist!
|
||||||
frm.trigger("refresh");
|
Chart Of accounts can be imported for company with no transactions`));
|
||||||
|
} else {
|
||||||
|
frm.trigger("refresh");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,7 +123,7 @@ var validate_csv_data = function(frm) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var create_import_button = function(frm) {
|
var create_import_button = function(frm) {
|
||||||
frm.page.set_primary_action(__("Start Import"), function () {
|
frm.page.set_primary_action(__("Import"), function () {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||||
args: {
|
args: {
|
||||||
@@ -118,7 +164,8 @@ var generate_tree_preview = function(frm) {
|
|||||||
args: {
|
args: {
|
||||||
file_name: frm.doc.import_file,
|
file_name: frm.doc.import_file,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
doctype: 'Chart of Accounts Importer'
|
doctype: 'Chart of Accounts Importer',
|
||||||
|
file_type: frm.doc.file_type
|
||||||
},
|
},
|
||||||
onclick: function(node) {
|
onclick: function(node) {
|
||||||
parent = node.value;
|
parent = node.value;
|
||||||
|
|||||||
@@ -1,226 +1,71 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_copy": 1,
|
"allow_copy": 1,
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2019-02-01 12:24:34.761380",
|
"creation": "2019-02-01 12:24:34.761380",
|
||||||
"custom": 0,
|
|
||||||
"description": "Import Chart of Accounts from a csv file",
|
"description": "Import Chart of Accounts from a csv file",
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Other",
|
"document_type": "Other",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"company",
|
||||||
|
"download_template",
|
||||||
|
"import_file",
|
||||||
|
"chart_preview",
|
||||||
|
"chart_tree"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
"options": "Company"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"depends_on": "company",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "import_file_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,
|
|
||||||
"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,
|
|
||||||
"depends_on": "",
|
|
||||||
"fieldname": "import_file",
|
"fieldname": "import_file",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 0,
|
"label": "Attach custom Chart of Accounts file"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Attach custom Chart of Accounts file",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"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": 1,
|
"collapsible": 1,
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "chart_preview",
|
"fieldname": "chart_preview",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Chart Preview"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Chart Preview",
|
|
||||||
"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,
|
|
||||||
"fieldname": "chart_tree",
|
"fieldname": "chart_tree",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"hidden": 0,
|
"label": "Chart Tree"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
{
|
||||||
"in_filter": 0,
|
"depends_on": "company",
|
||||||
"in_global_search": 0,
|
"fieldname": "download_template",
|
||||||
"in_list_view": 0,
|
"fieldtype": "Button",
|
||||||
"in_standard_filter": 0,
|
"label": "Download Template"
|
||||||
"label": "Chart Tree",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 1,
|
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"links": [],
|
||||||
"max_attachments": 0,
|
"modified": "2020-02-28 08:49:11.422846",
|
||||||
"modified": "2019-02-04 23:10:30.136807",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Chart of Accounts Importer",
|
"name": "Chart of Accounts Importer",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 0,
|
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"read_only_onload": 0,
|
"sort_field": "modified",
|
||||||
"show_name_in_global_search": 0,
|
"sort_order": "DESC"
|
||||||
"sort_field": "",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -4,18 +4,28 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
import frappe, csv
|
import frappe, csv, os
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils.csvutils import UnicodeWriter
|
from frappe.utils.csvutils import UnicodeWriter
|
||||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json
|
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json
|
||||||
|
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file
|
||||||
|
|
||||||
class ChartofAccountsImporter(Document):
|
class ChartofAccountsImporter(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_company(company):
|
def validate_company(company):
|
||||||
|
parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company',
|
||||||
|
{'name': company}, ['parent_company',
|
||||||
|
'allow_account_creation_against_child_company'])
|
||||||
|
|
||||||
|
if parent_company and (not allow_account_creation_against_child_company):
|
||||||
|
frappe.throw(_("""{0} is a child company. Please import accounts against parent company
|
||||||
|
or enable {1} in company master""").format(frappe.bold(company),
|
||||||
|
frappe.bold('Allow Account Creation Against Child Company')), title='Wrong Company')
|
||||||
|
|
||||||
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
|
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -25,42 +35,85 @@ def import_coa(file_name, company):
|
|||||||
unset_existing_data(company)
|
unset_existing_data(company)
|
||||||
|
|
||||||
# create accounts
|
# create accounts
|
||||||
forest = build_forest(generate_data_from_csv(file_name))
|
file_doc, extension = get_file(file_name)
|
||||||
|
|
||||||
|
if extension == 'csv':
|
||||||
|
data = generate_data_from_csv(file_doc)
|
||||||
|
else:
|
||||||
|
data = generate_data_from_excel(file_doc, extension)
|
||||||
|
|
||||||
|
forest = build_forest(data)
|
||||||
create_charts(company, custom_chart=forest)
|
create_charts(company, custom_chart=forest)
|
||||||
|
|
||||||
# trigger on_update for company to reset default accounts
|
# trigger on_update for company to reset default accounts
|
||||||
set_default_accounts(company)
|
set_default_accounts(company)
|
||||||
|
|
||||||
def generate_data_from_csv(file_name, as_dict=False):
|
def get_file(file_name):
|
||||||
''' read csv file and return the generated nested tree '''
|
file_doc = frappe.get_doc("File", {"file_url": file_name})
|
||||||
if not file_name.endswith('.csv'):
|
parts = file_doc.get_extension()
|
||||||
frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload")
|
extension = parts[1]
|
||||||
|
extension = extension.lstrip(".")
|
||||||
|
|
||||||
|
if extension not in ('csv', 'xlsx', 'xls'):
|
||||||
|
frappe.throw("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload")
|
||||||
|
|
||||||
|
return file_doc, extension
|
||||||
|
|
||||||
|
def generate_data_from_csv(file_doc, as_dict=False):
|
||||||
|
''' read csv file and return the generated nested tree '''
|
||||||
|
|
||||||
file_doc = frappe.get_doc('File', {"file_url": file_name})
|
|
||||||
file_path = file_doc.get_full_path()
|
file_path = file_doc.get_full_path()
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
with open(file_path, 'r') as in_file:
|
with open(file_path, 'r') as in_file:
|
||||||
csv_reader = list(csv.reader(in_file))
|
csv_reader = list(csv.reader(in_file))
|
||||||
headers = csv_reader[1][1:]
|
headers = csv_reader[0]
|
||||||
del csv_reader[0:2] # delete top row and headers row
|
del csv_reader[0] # delete top row and headers row
|
||||||
|
|
||||||
for row in csv_reader:
|
for row in csv_reader:
|
||||||
if as_dict:
|
if as_dict:
|
||||||
data.append({frappe.scrub(header): row[index+1] for index, header in enumerate(headers)})
|
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
|
||||||
else:
|
else:
|
||||||
if not row[2]: row[2] = row[1]
|
if not row[1]: row[1] = row[0]
|
||||||
data.append(row[1:])
|
data.append(row)
|
||||||
|
|
||||||
# convert csv data
|
# convert csv data
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def generate_data_from_excel(file_doc, extension, as_dict=False):
|
||||||
|
content = file_doc.get_content()
|
||||||
|
|
||||||
|
if extension == "xlsx":
|
||||||
|
rows = read_xlsx_file_from_attached_file(fcontent=content)
|
||||||
|
elif extension == "xls":
|
||||||
|
rows = read_xls_file_from_attached_file(content)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
headers = rows[0]
|
||||||
|
del rows[0]
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
if as_dict:
|
||||||
|
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
|
||||||
|
else:
|
||||||
|
if not row[1]: row[1] = row[0]
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_coa(doctype, parent, is_root=False, file_name=None):
|
def get_coa(doctype, parent, is_root=False, file_name=None):
|
||||||
''' called by tree view (to fetch node's children) '''
|
''' called by tree view (to fetch node's children) '''
|
||||||
|
|
||||||
|
file_doc, extension = get_file(file_name)
|
||||||
parent = None if parent==_('All Accounts') else parent
|
parent = None if parent==_('All Accounts') else parent
|
||||||
forest = build_forest(generate_data_from_csv(file_name))
|
|
||||||
|
if extension == 'csv':
|
||||||
|
data = generate_data_from_csv(file_doc)
|
||||||
|
else:
|
||||||
|
data = generate_data_from_excel(file_doc, extension)
|
||||||
|
|
||||||
|
forest = build_forest(data)
|
||||||
accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
|
accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
|
||||||
|
|
||||||
# filter out to show data for the selected node only
|
# filter out to show data for the selected node only
|
||||||
@@ -91,15 +144,18 @@ def build_forest(data):
|
|||||||
|
|
||||||
# returns the path of any node in list format
|
# returns the path of any node in list format
|
||||||
def return_parent(data, child):
|
def return_parent(data, child):
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
for row in data:
|
for row in data:
|
||||||
account_name, parent_account = row[0:2]
|
account_name, parent_account = row[0:2]
|
||||||
if parent_account == account_name == child:
|
if parent_account == account_name == child:
|
||||||
return [parent_account]
|
return [parent_account]
|
||||||
elif account_name == child:
|
elif account_name == child:
|
||||||
parent_account_list = return_parent(data, parent_account)
|
parent_account_list = return_parent(data, parent_account)
|
||||||
if not parent_account_list and parent_account:
|
if not parent_account_list:
|
||||||
frappe.throw(_("The parent account {0} does not exists")
|
frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
|
||||||
.format(parent_account))
|
frappe.bold(parent_account)))
|
||||||
|
|
||||||
return [child] + parent_account_list
|
return [child] + parent_account_list
|
||||||
|
|
||||||
charts_map, paths = {}, []
|
charts_map, paths = {}, []
|
||||||
@@ -114,7 +170,7 @@ def build_forest(data):
|
|||||||
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
||||||
|
|
||||||
charts_map[account_name] = {}
|
charts_map[account_name] = {}
|
||||||
if is_group == 1: charts_map[account_name]["is_group"] = is_group
|
if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
|
||||||
if account_type: charts_map[account_name]["account_type"] = account_type
|
if account_type: charts_map[account_name]["account_type"] = account_type
|
||||||
if root_type: charts_map[account_name]["root_type"] = root_type
|
if root_type: charts_map[account_name]["root_type"] = root_type
|
||||||
if account_number: charts_map[account_name]["account_number"] = account_number
|
if account_number: charts_map[account_name]["account_number"] = account_number
|
||||||
@@ -132,24 +188,94 @@ def build_forest(data):
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def build_response_as_excel(writer):
|
||||||
|
filename = frappe.generate_hash("", 10)
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(cstr(writer.getvalue()).encode('utf-8'))
|
||||||
|
f = open(filename)
|
||||||
|
reader = csv.reader(f)
|
||||||
|
|
||||||
|
from frappe.utils.xlsxutils import make_xlsx
|
||||||
|
xlsx_file = make_xlsx(reader, "Chart Of Accounts Importer Template")
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
os.remove(filename)
|
||||||
|
|
||||||
|
# write out response as a xlsx type
|
||||||
|
frappe.response['filename'] = 'coa_importer_template.xlsx'
|
||||||
|
frappe.response['filecontent'] = xlsx_file.getvalue()
|
||||||
|
frappe.response['type'] = 'binary'
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_template():
|
def download_template(file_type, template_type):
|
||||||
data = frappe._dict(frappe.local.form_dict)
|
data = frappe._dict(frappe.local.form_dict)
|
||||||
|
|
||||||
|
writer = get_template(template_type)
|
||||||
|
|
||||||
|
if file_type == 'CSV':
|
||||||
|
# download csv file
|
||||||
|
frappe.response['result'] = cstr(writer.getvalue())
|
||||||
|
frappe.response['type'] = 'csv'
|
||||||
|
frappe.response['doctype'] = 'Chart of Accounts Importer'
|
||||||
|
else:
|
||||||
|
build_response_as_excel(writer)
|
||||||
|
|
||||||
|
def get_template(template_type):
|
||||||
|
|
||||||
fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
|
fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
|
||||||
writer = UnicodeWriter()
|
writer = UnicodeWriter()
|
||||||
|
writer.writerow(fields)
|
||||||
|
|
||||||
writer.writerow([_('Chart of Accounts Template')])
|
if template_type == 'Blank Template':
|
||||||
writer.writerow([_("Column Labels : ")] + fields)
|
for root_type in get_root_types():
|
||||||
writer.writerow([_("Start entering data from here : ")])
|
writer.writerow(['', '', '', 1, '', root_type])
|
||||||
|
|
||||||
|
for account in get_mandatory_group_accounts():
|
||||||
|
writer.writerow(['', '', '', 1, account, "Asset"])
|
||||||
|
|
||||||
|
for account_type in get_mandatory_account_types():
|
||||||
|
writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')])
|
||||||
|
else:
|
||||||
|
writer = get_sample_template(writer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
|
||||||
|
def get_sample_template(writer):
|
||||||
|
template = [
|
||||||
|
["Application Of Funds(Assets)", "", "", 1, "", "Asset"],
|
||||||
|
["Sources Of Funds(Liabilities)", "", "", 1, "", "Liability"],
|
||||||
|
["Equity", "", "", 1, "", "Equity"],
|
||||||
|
["Expenses", "", "", 1, "", "Expense"],
|
||||||
|
["Income", "", "", 1, "", "Income"],
|
||||||
|
["Bank Accounts", "Application Of Funds(Assets)", "", 1, "Bank", "Asset"],
|
||||||
|
["Cash In Hand", "Application Of Funds(Assets)", "", 1, "Cash", "Asset"],
|
||||||
|
["Stock Assets", "Application Of Funds(Assets)", "", 1, "Stock", "Asset"],
|
||||||
|
["Cost Of Goods Sold", "Expenses", "", 0, "Cost of Goods Sold", "Expense"],
|
||||||
|
["Asset Depreciation", "Expenses", "", 0, "Depreciation", "Expense"],
|
||||||
|
["Fixed Assets", "Application Of Funds(Assets)", "", 0, "Fixed Asset", "Asset"],
|
||||||
|
["Accounts Payable", "Sources Of Funds(Liabilities)", "", 0, "Payable", "Liability"],
|
||||||
|
["Accounts Receivable", "Application Of Funds(Assets)", "", 1, "Receivable", "Asset"],
|
||||||
|
["Stock Expenses", "Expenses", "", 0, "Stock Adjustment", "Expense"],
|
||||||
|
["Sample Bank", "Bank Accounts", "", 0, "Bank", "Asset"],
|
||||||
|
["Cash", "Cash In Hand", "", 0, "Cash", "Asset"],
|
||||||
|
["Stores", "Stock Assets", "", 0, "Stock", "Asset"],
|
||||||
|
]
|
||||||
|
|
||||||
|
for row in template:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
|
||||||
# download csv file
|
|
||||||
frappe.response['result'] = cstr(writer.getvalue())
|
|
||||||
frappe.response['type'] = 'csv'
|
|
||||||
frappe.response['doctype'] = data.get('doctype')
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_accounts(file_name):
|
def validate_accounts(file_name):
|
||||||
accounts = generate_data_from_csv(file_name, as_dict=True)
|
|
||||||
|
file_doc, extension = get_file(file_name)
|
||||||
|
|
||||||
|
if extension == 'csv':
|
||||||
|
accounts = generate_data_from_csv(file_doc, as_dict=True)
|
||||||
|
else:
|
||||||
|
accounts = generate_data_from_excel(file_doc, extension, as_dict=True)
|
||||||
|
|
||||||
accounts_dict = {}
|
accounts_dict = {}
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
@@ -174,12 +300,38 @@ def validate_root(accounts):
|
|||||||
for account in roots:
|
for account in roots:
|
||||||
if not account.get("root_type") and account.get("account_name"):
|
if not account.get("root_type") and account.get("account_name"):
|
||||||
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
|
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
|
||||||
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"):
|
elif account.get("root_type") not in get_root_types() and account.get("account_name"):
|
||||||
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
|
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
|
||||||
|
|
||||||
if error_messages:
|
if error_messages:
|
||||||
return "<br>".join(error_messages)
|
return "<br>".join(error_messages)
|
||||||
|
|
||||||
|
def get_root_types():
|
||||||
|
return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
|
||||||
|
|
||||||
|
def get_report_type(root_type):
|
||||||
|
if root_type in ('Asset', 'Liability', 'Equity'):
|
||||||
|
return 'Balance Sheet'
|
||||||
|
else:
|
||||||
|
return 'Profit and Loss'
|
||||||
|
|
||||||
|
def get_mandatory_group_accounts():
|
||||||
|
return ('Bank', 'Cash', 'Stock')
|
||||||
|
|
||||||
|
def get_mandatory_account_types():
|
||||||
|
return [
|
||||||
|
{'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'},
|
||||||
|
{'account_type': 'Depreciation', 'root_type': 'Expense'},
|
||||||
|
{'account_type': 'Fixed Asset', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Payable', 'root_type': 'Liability'},
|
||||||
|
{'account_type': 'Receivable', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Stock Adjustment', 'root_type': 'Expense'},
|
||||||
|
{'account_type': 'Bank', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Cash', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Stock', 'root_type': 'Asset'}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def validate_account_types(accounts):
|
def validate_account_types(accounts):
|
||||||
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
||||||
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if (!frm.is_new()) {
|
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");
|
frm.trigger("update_cost_center_number");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -47,35 +47,45 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
},
|
},
|
||||||
update_cost_center_number: function(frm) {
|
update_cost_center_number: function(frm) {
|
||||||
var d = new frappe.ui.Dialog({
|
var d = new frappe.ui.Dialog({
|
||||||
title: __('Update Cost Center Number'),
|
title: __('Update Cost Center Name / Number'),
|
||||||
fields: [
|
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",
|
"fieldname": "cost_center_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"default": frm.doc.cost_center_number
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
var data = d.get_values();
|
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();
|
d.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
frappe.dom.freeze();
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.utils.update_number_field",
|
method: "erpnext.accounts.utils.update_cost_center",
|
||||||
args: {
|
args: {
|
||||||
doctype_name: frm.doc.doctype,
|
docname: frm.doc.name,
|
||||||
name: frm.doc.name,
|
cost_center_name: data.cost_center_name,
|
||||||
field_name: d.fields[0].fieldname,
|
cost_center_number: data.cost_center_number,
|
||||||
number_value: data.cost_center_number,
|
|
||||||
company: frm.doc.company
|
company: frm.doc.company
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
frappe.dom.unfreeze();
|
||||||
if(!r.exc) {
|
if(!r.exc) {
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
frappe.set_route("Form", "Cost Center", r.message);
|
frappe.set_route("Form", "Cost Center", r.message);
|
||||||
} else {
|
} else {
|
||||||
|
me.frm.set_value("cost_center_name", data.cost_center_name);
|
||||||
me.frm.set_value("cost_center_number", data.cost_center_number);
|
me.frm.set_value("cost_center_number", data.cost_center_number);
|
||||||
}
|
}
|
||||||
d.hide();
|
d.hide();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_copy": 1,
|
"allow_copy": 1,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
|
||||||
"creation": "2013-01-23 19:57:17",
|
"creation": "2013-01-23 19:57:17",
|
||||||
"description": "Track separate Income and Expense for product verticals or divisions.",
|
"description": "Track separate Income and Expense for product verticals or divisions.",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -126,7 +125,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-18 18:26:01.540170",
|
"modified": "2020-04-29 16:09:30.025214",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cost Center",
|
"name": "Cost Center",
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ class GLEntry(Document):
|
|||||||
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
||||||
|
|
||||||
if ret.is_group==1:
|
if ret.is_group==1:
|
||||||
frappe.throw(_("{0} {1}: Account {2} cannot be a Group")
|
frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in
|
||||||
.format(self.voucher_type, self.voucher_no, self.account))
|
transactions''').format(self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
if ret.docstatus==2:
|
if ret.docstatus==2:
|
||||||
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from frappe import _
|
|||||||
from frappe.utils import flt, getdate, nowdate, add_days
|
from frappe.utils import flt, getdate, nowdate, add_days
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class InvoiceDiscounting(AccountsController):
|
class InvoiceDiscounting(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -81,10 +82,15 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||||
|
|
||||||
|
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
invoice_fields.extend(accounting_dimensions)
|
||||||
|
|
||||||
for d in self.invoices:
|
for d in self.invoices:
|
||||||
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
|
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
|
||||||
["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
|
|
||||||
|
|
||||||
if d.outstanding_amount:
|
if d.outstanding_amount:
|
||||||
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
|
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
|
||||||
@@ -102,7 +108,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
"cost_center": inv.cost_center,
|
"cost_center": inv.cost_center,
|
||||||
"against_voucher": d.sales_invoice,
|
"against_voucher": d.sales_invoice,
|
||||||
"against_voucher_type": "Sales Invoice"
|
"against_voucher_type": "Sales Invoice"
|
||||||
}, inv.party_account_currency))
|
}, inv.party_account_currency, item=inv))
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": self.accounts_receivable_credit,
|
"account": self.accounts_receivable_credit,
|
||||||
@@ -115,7 +121,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
"cost_center": inv.cost_center,
|
"cost_center": inv.cost_center,
|
||||||
"against_voucher": d.sales_invoice,
|
"against_voucher": d.sales_invoice,
|
||||||
"against_voucher_type": "Sales Invoice"
|
"against_voucher_type": "Sales Invoice"
|
||||||
}, ar_credit_account_currency))
|
}, ar_credit_account_currency, item=inv))
|
||||||
|
|
||||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
||||||
|
|
||||||
|
|||||||
@@ -561,20 +561,20 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
if self.write_off_based_on == 'Accounts Receivable':
|
if self.write_off_based_on == 'Accounts Receivable':
|
||||||
jd1.party_type = "Customer"
|
jd1.party_type = "Customer"
|
||||||
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
||||||
jd1.reference_type = "Sales Invoice"
|
jd1.reference_type = "Sales Invoice"
|
||||||
jd1.reference_name = cstr(d.name)
|
jd1.reference_name = cstr(d.name)
|
||||||
elif self.write_off_based_on == 'Accounts Payable':
|
elif self.write_off_based_on == 'Accounts Payable':
|
||||||
jd1.party_type = "Supplier"
|
jd1.party_type = "Supplier"
|
||||||
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
||||||
jd1.reference_type = "Purchase Invoice"
|
jd1.reference_type = "Purchase Invoice"
|
||||||
jd1.reference_name = cstr(d.name)
|
jd1.reference_name = cstr(d.name)
|
||||||
|
|
||||||
jd2 = self.append('accounts', {})
|
jd2 = self.append('accounts', {})
|
||||||
if self.write_off_based_on == 'Accounts Receivable':
|
if self.write_off_based_on == 'Accounts Receivable':
|
||||||
jd2.debit = total
|
jd2.debit_in_account_currency = total
|
||||||
elif self.write_off_based_on == 'Accounts Payable':
|
elif self.write_off_based_on == 'Accounts Payable':
|
||||||
jd2.credit = total
|
jd2.credit_in_account_currency = total
|
||||||
|
|
||||||
self.validate_total_debit_and_credit()
|
self.validate_total_debit_and_credit()
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,21 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query('payment_term', 'references', function(frm, cdt, cdn) {
|
||||||
|
const child = locals[cdt][cdn];
|
||||||
|
if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) {
|
||||||
|
let payment_term_list = frappe.get_list('Payment Schedule', {'parent': child.reference_name});
|
||||||
|
|
||||||
|
payment_term_list = payment_term_list.map(pt => pt.payment_term);
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'name': ['in', payment_term_list]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
frm.set_query("reference_name", "references", function(doc, cdt, cdn) {
|
frm.set_query("reference_name", "references", function(doc, cdt, cdn) {
|
||||||
const child = locals[cdt][cdn];
|
const child = locals[cdt][cdn];
|
||||||
const filters = {"docstatus": 1, "company": doc.company};
|
const filters = {"docstatus": 1, "company": doc.company};
|
||||||
@@ -272,7 +287,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_value("contact_email", "");
|
frm.set_value("contact_email", "");
|
||||||
frm.set_value("contact_person", "");
|
frm.set_value("contact_person", "");
|
||||||
}
|
}
|
||||||
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
|
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party && frm.doc.company) {
|
||||||
if(!frm.doc.posting_date) {
|
if(!frm.doc.posting_date) {
|
||||||
frappe.msgprint(__("Please select Posting Date before selecting Party"))
|
frappe.msgprint(__("Please select Posting Date before selecting Party"))
|
||||||
frm.set_value("party", "");
|
frm.set_value("party", "");
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.set_remarks()
|
self.set_remarks()
|
||||||
self.validate_duplicate_entry()
|
self.validate_duplicate_entry()
|
||||||
self.validate_allocated_amount()
|
self.validate_allocated_amount()
|
||||||
|
self.validate_paid_invoices()
|
||||||
self.ensure_supplier_is_not_blocked()
|
self.ensure_supplier_is_not_blocked()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
@@ -71,9 +72,9 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_expense_claim()
|
||||||
|
self.update_payment_schedule()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.setup_party_account_field()
|
self.setup_party_account_field()
|
||||||
self.make_gl_entries(cancel=1)
|
self.make_gl_entries(cancel=1)
|
||||||
@@ -81,6 +82,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_expense_claim()
|
||||||
self.delink_advance_entry_references()
|
self.delink_advance_entry_references()
|
||||||
|
self.update_payment_schedule(cancel=1)
|
||||||
self.set_payment_req_status()
|
self.set_payment_req_status()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
@@ -94,10 +96,10 @@ class PaymentEntry(AccountsController):
|
|||||||
def validate_duplicate_entry(self):
|
def validate_duplicate_entry(self):
|
||||||
reference_names = []
|
reference_names = []
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if (d.reference_doctype, d.reference_name) in reference_names:
|
if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names:
|
||||||
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
|
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
|
||||||
.format(d.idx, d.reference_doctype, d.reference_name))
|
.format(d.idx, d.reference_doctype, d.reference_name))
|
||||||
reference_names.append((d.reference_doctype, d.reference_name))
|
reference_names.append((d.reference_doctype, d.reference_name, d.payment_term))
|
||||||
|
|
||||||
def set_bank_account_data(self):
|
def set_bank_account_data(self):
|
||||||
if self.bank_account:
|
if self.bank_account:
|
||||||
@@ -264,6 +266,25 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("{0} {1} must be submitted")
|
frappe.throw(_("{0} {1} must be submitted")
|
||||||
.format(d.reference_doctype, d.reference_name))
|
.format(d.reference_doctype, d.reference_name))
|
||||||
|
|
||||||
|
def validate_paid_invoices(self):
|
||||||
|
no_oustanding_refs = {}
|
||||||
|
|
||||||
|
for d in self.get("references"):
|
||||||
|
if not d.allocated_amount:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
|
||||||
|
outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
|
||||||
|
if outstanding_amount <= 0 and not is_return:
|
||||||
|
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
|
||||||
|
|
||||||
|
for k, v in no_oustanding_refs.items():
|
||||||
|
frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
|
||||||
|
If this is undesirable please cancel the corresponding Payment Entry.")
|
||||||
|
.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")),
|
||||||
|
title=_("Warning"), indicator="orange")
|
||||||
|
|
||||||
|
|
||||||
def validate_journal_entry(self):
|
def validate_journal_entry(self):
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
||||||
@@ -285,6 +306,36 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||||
.format(d.reference_name, dr_or_cr))
|
.format(d.reference_name, dr_or_cr))
|
||||||
|
|
||||||
|
def update_payment_schedule(self, cancel=0):
|
||||||
|
invoice_payment_amount_map = {}
|
||||||
|
invoice_paid_amount_map = {}
|
||||||
|
|
||||||
|
for reference in self.get('references'):
|
||||||
|
if reference.payment_term and reference.reference_name:
|
||||||
|
key = (reference.payment_term, reference.reference_name)
|
||||||
|
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||||
|
invoice_payment_amount_map[key] += reference.allocated_amount
|
||||||
|
|
||||||
|
if not invoice_paid_amount_map.get(reference.reference_name):
|
||||||
|
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
|
||||||
|
fields=['paid_amount', 'payment_amount', 'payment_term'])
|
||||||
|
for term in payment_schedule:
|
||||||
|
invoice_key = (term.payment_term, reference.reference_name)
|
||||||
|
invoice_paid_amount_map.setdefault(invoice_key, {})
|
||||||
|
invoice_paid_amount_map[invoice_key]['outstanding'] = term.payment_amount - term.paid_amount
|
||||||
|
|
||||||
|
for key, amount in iteritems(invoice_payment_amount_map):
|
||||||
|
if cancel:
|
||||||
|
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
|
||||||
|
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||||
|
else:
|
||||||
|
outstanding = invoice_paid_amount_map.get(key)['outstanding']
|
||||||
|
if amount > outstanding:
|
||||||
|
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
||||||
|
|
||||||
|
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
||||||
|
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
self.status = 'Cancelled'
|
self.status = 'Cancelled'
|
||||||
@@ -397,8 +448,6 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
||||||
|
|
||||||
def set_remarks(self):
|
def set_remarks(self):
|
||||||
if self.remarks: return
|
|
||||||
|
|
||||||
if self.payment_type=="Internal Transfer":
|
if self.payment_type=="Internal Transfer":
|
||||||
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
||||||
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
|
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
|
||||||
@@ -452,7 +501,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"against": against_account,
|
"against": against_account,
|
||||||
"account_currency": self.party_account_currency,
|
"account_currency": self.party_account_currency,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
|
|
||||||
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
||||||
|
|
||||||
@@ -496,7 +545,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"credit_in_account_currency": self.paid_amount,
|
"credit_in_account_currency": self.paid_amount,
|
||||||
"credit": self.base_paid_amount,
|
"credit": self.base_paid_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
if self.payment_type in ("Receive", "Internal Transfer"):
|
if self.payment_type in ("Receive", "Internal Transfer"):
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -507,7 +556,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"debit_in_account_currency": self.received_amount,
|
"debit_in_account_currency": self.received_amount,
|
||||||
"debit": self.base_received_amount,
|
"debit": self.base_received_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_deductions_gl_entries(self, gl_entries):
|
def add_deductions_gl_entries(self, gl_entries):
|
||||||
@@ -1012,15 +1061,22 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
||||||
frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date)))
|
frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date)))
|
||||||
else:
|
else:
|
||||||
pe.append("references", {
|
if (doc.doctype in ('Sales Invoice', 'Purchase Invoice')
|
||||||
'reference_doctype': dt,
|
and frappe.get_value('Payment Terms Template',
|
||||||
'reference_name': dn,
|
{'name': doc.payment_terms_template}, 'allocate_payment_based_on_payment_terms')):
|
||||||
"bill_no": doc.get("bill_no"),
|
|
||||||
"due_date": doc.get("due_date"),
|
for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
'total_amount': grand_total,
|
pe.append('references', reference)
|
||||||
'outstanding_amount': outstanding_amount,
|
else:
|
||||||
'allocated_amount': outstanding_amount
|
pe.append("references", {
|
||||||
})
|
'reference_doctype': dt,
|
||||||
|
'reference_name': dn,
|
||||||
|
"bill_no": doc.get("bill_no"),
|
||||||
|
"due_date": doc.get("due_date"),
|
||||||
|
'total_amount': grand_total,
|
||||||
|
'outstanding_amount': outstanding_amount,
|
||||||
|
'allocated_amount': outstanding_amount
|
||||||
|
})
|
||||||
|
|
||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
@@ -1029,6 +1085,22 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
return pe
|
return pe
|
||||||
|
|
||||||
|
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
|
references = []
|
||||||
|
for payment_term in payment_schedule:
|
||||||
|
references.append({
|
||||||
|
'reference_doctype': dt,
|
||||||
|
'reference_name': dn,
|
||||||
|
'bill_no': doc.get('bill_no'),
|
||||||
|
'due_date': doc.get('due_date'),
|
||||||
|
'total_amount': grand_total,
|
||||||
|
'outstanding_amount': outstanding_amount,
|
||||||
|
'payment_term': payment_term.payment_term,
|
||||||
|
'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount,
|
||||||
|
payment_term.precision('payment_amount'))
|
||||||
|
})
|
||||||
|
|
||||||
|
return references
|
||||||
|
|
||||||
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
|
|||||||
12
erpnext/accounts/doctype/payment_entry/payment_entry_list.js
Normal file
12
erpnext/accounts/doctype/payment_entry/payment_entry_list.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
frappe.listview_settings['Payment Entry'] = {
|
||||||
|
|
||||||
|
onload: function(listview) {
|
||||||
|
listview.page.fields_dict.party_type.get_query = function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -149,6 +149,30 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
|
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
|
||||||
self.assertEqual(outstanding_amount, 0)
|
self.assertEqual(outstanding_amount, 0)
|
||||||
|
|
||||||
|
def test_payment_entry_against_payment_terms(self):
|
||||||
|
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
|
||||||
|
create_payment_terms_template()
|
||||||
|
si.payment_terms_template = 'Test Receivable Template'
|
||||||
|
|
||||||
|
si.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 18
|
||||||
|
})
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
|
||||||
|
pe.submit()
|
||||||
|
si.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(pe.references[0].payment_term, 'Basic Amount Receivable')
|
||||||
|
self.assertEqual(pe.references[1].payment_term, 'Tax Receivable')
|
||||||
|
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
||||||
|
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
||||||
|
|
||||||
def test_payment_against_sales_invoice_to_check_status(self):
|
def test_payment_against_sales_invoice_to_check_status(self):
|
||||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||||
@@ -610,3 +634,37 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
|
|
||||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||||
accounts_settings.save()
|
accounts_settings.save()
|
||||||
|
|
||||||
|
def create_payment_terms_template():
|
||||||
|
|
||||||
|
create_payment_term('Basic Amount Receivable')
|
||||||
|
create_payment_term('Tax Receivable')
|
||||||
|
|
||||||
|
if not frappe.db.exists('Payment Terms Template', 'Test Receivable Template'):
|
||||||
|
payment_term_template = frappe.get_doc({
|
||||||
|
'doctype': 'Payment Terms Template',
|
||||||
|
'template_name': 'Test Receivable Template',
|
||||||
|
'allocate_payment_based_on_payment_terms': 1,
|
||||||
|
'terms': [{
|
||||||
|
'doctype': 'Payment Terms Template Detail',
|
||||||
|
'payment_term': 'Basic Amount Receivable',
|
||||||
|
'invoice_portion': 84.746,
|
||||||
|
'credit_days_based_on': 'Day(s) after invoice date',
|
||||||
|
'credit_days': 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'doctype': 'Payment Terms Template Detail',
|
||||||
|
'payment_term': 'Tax Receivable',
|
||||||
|
'invoice_portion': 15.254,
|
||||||
|
'credit_days_based_on': 'Day(s) after invoice date',
|
||||||
|
'credit_days': 2
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
|
||||||
|
def create_payment_term(name):
|
||||||
|
if not frappe.db.exists('Payment Term', name):
|
||||||
|
frappe.get_doc({
|
||||||
|
'doctype': 'Payment Term',
|
||||||
|
'payment_term_name': name
|
||||||
|
}).insert()
|
||||||
|
|||||||
@@ -1,343 +1,107 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-06-01 16:55:32.196722",
|
"creation": "2016-06-01 16:55:32.196722",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"reference_doctype",
|
||||||
|
"reference_name",
|
||||||
|
"due_date",
|
||||||
|
"bill_no",
|
||||||
|
"payment_term",
|
||||||
|
"column_break_4",
|
||||||
|
"total_amount",
|
||||||
|
"outstanding_amount",
|
||||||
|
"allocated_amount",
|
||||||
|
"exchange_rate"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "reference_doctype",
|
"fieldname": "reference_doctype",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "DocType",
|
"options": "DocType",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "reference_name",
|
"fieldname": "reference_name",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "reference_doctype",
|
"options": "reference_doctype",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "due_date",
|
"fieldname": "due_date",
|
||||||
"fieldtype": "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": "Due Date",
|
"label": "Due Date",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"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,
|
|
||||||
"depends_on": "",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "bill_no",
|
"fieldname": "bill_no",
|
||||||
"fieldtype": "Data",
|
"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": "Supplier Invoice No",
|
"label": "Supplier Invoice No",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"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": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "total_amount",
|
"fieldname": "total_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Amount",
|
"label": "Total Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "outstanding_amount",
|
"fieldname": "outstanding_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Outstanding",
|
"label": "Outstanding",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "allocated_amount",
|
"fieldname": "allocated_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Allocated"
|
||||||
"label": "Allocated",
|
|
||||||
"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,
|
|
||||||
"depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')",
|
"depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "exchange_rate",
|
"fieldname": "exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"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": "Exchange Rate",
|
"label": "Exchange Rate",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
},
|
||||||
"remember_last_selected_value": 0,
|
{
|
||||||
"report_hide": 0,
|
"fieldname": "payment_term",
|
||||||
"reqd": 0,
|
"fieldtype": "Link",
|
||||||
"search_index": 0,
|
"label": "Payment Term",
|
||||||
"set_only_once": 0,
|
"options": "Payment Term"
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2019-05-01 13:24:56.586677",
|
"modified": "2020-03-13 12:07:19.362539",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
|
|||||||
paid_amt += d.amount
|
paid_amt += d.amount
|
||||||
|
|
||||||
je.append('accounts', {
|
je.append('accounts', {
|
||||||
'account': doc.references[0].account,
|
'account': doc.account,
|
||||||
'credit_in_account_currency': paid_amt
|
'credit_in_account_currency': paid_amt
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class PaymentRequest(Document):
|
|||||||
elif self.payment_request_type == 'Inward':
|
elif self.payment_request_type == 'Inward':
|
||||||
self.db_set('status', 'Requested')
|
self.db_set('status', 'Requested')
|
||||||
|
|
||||||
send_mail = self.payment_gateway_validation()
|
send_mail = self.payment_gateway_validation() if self.payment_gateway else None
|
||||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||||
|
|
||||||
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
||||||
@@ -326,7 +326,7 @@ def make_payment_request(**args):
|
|||||||
"reference_doctype": args.dt,
|
"reference_doctype": args.dt,
|
||||||
"reference_name": args.dn,
|
"reference_name": args.dn,
|
||||||
"party_type": args.get("party_type") or "Customer",
|
"party_type": args.get("party_type") or "Customer",
|
||||||
"party": args.get("party") or ref_doc.customer,
|
"party": args.get("party") or ref_doc.get("customer"),
|
||||||
"bank_account": bank_account
|
"bank_account": bank_account
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,243 +1,82 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "",
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2017-08-10 15:38:00.080575",
|
"creation": "2017-08-10 15:38:00.080575",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"payment_term",
|
||||||
|
"description",
|
||||||
|
"due_date",
|
||||||
|
"invoice_portion",
|
||||||
|
"payment_amount",
|
||||||
|
"mode_of_payment",
|
||||||
|
"paid_amount"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fieldname": "payment_term",
|
"fieldname": "payment_term",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payment Term",
|
"label": "Payment Term",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Term",
|
"options": "Payment Term",
|
||||||
"permlevel": 0,
|
"print_hide": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"fetch_from": "",
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Description"
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"fieldname": "due_date",
|
"fieldname": "due_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Due Date",
|
"label": "Due Date",
|
||||||
"length": 0,
|
"reqd": 1
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_from": "",
|
|
||||||
"fieldname": "invoice_portion",
|
"fieldname": "invoice_portion",
|
||||||
"fieldtype": "Percent",
|
"fieldtype": "Percent",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Invoice Portion",
|
"label": "Invoice Portion",
|
||||||
"length": 0,
|
"print_hide": 1
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"fieldname": "payment_amount",
|
"fieldname": "payment_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payment Amount",
|
"label": "Payment Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "mode_of_payment",
|
||||||
"fieldtype": "Link",
|
"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": "Mode of Payment",
|
"label": "Mode of Payment",
|
||||||
"length": 0,
|
"options": "Mode of Payment"
|
||||||
"no_copy": 0,
|
},
|
||||||
"options": "Mode of Payment",
|
{
|
||||||
"permlevel": 0,
|
"fieldname": "paid_amount",
|
||||||
"precision": "",
|
"fieldtype": "Currency",
|
||||||
"print_hide": 0,
|
"label": "Paid Amount"
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2018-09-06 17:35:44.580209",
|
"modified": "2020-03-13 17:58:24.729526",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Schedule",
|
"name": "Payment Schedule",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -1,164 +1,84 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:template_name",
|
"autoname": "field:template_name",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2017-08-10 15:34:28.058054",
|
"creation": "2017-08-10 15:34:28.058054",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"template_name",
|
||||||
|
"allocate_payment_based_on_payment_terms",
|
||||||
|
"terms"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "template_name",
|
"fieldname": "template_name",
|
||||||
"fieldtype": "Data",
|
"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": "Template Name",
|
"label": "Template Name",
|
||||||
"length": 0,
|
"unique": 1
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "terms",
|
"fieldname": "terms",
|
||||||
"fieldtype": "Table",
|
"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": "Payment Terms",
|
"label": "Payment Terms",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Terms Template Detail",
|
"options": "Payment Terms Template Detail",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
},
|
||||||
"print_hide": 0,
|
{
|
||||||
"print_hide_if_no_value": 0,
|
"default": "0",
|
||||||
"read_only": 0,
|
"description": "If this checkbox is checked, paid amount will be splitted and allocated as per the amounts in payment schedule against each payment term",
|
||||||
"remember_last_selected_value": 0,
|
"fieldname": "allocate_payment_based_on_payment_terms",
|
||||||
"report_hide": 0,
|
"fieldtype": "Check",
|
||||||
"reqd": 1,
|
"label": "Allocate Payment Based On Payment Terms"
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2020-04-01 15:35:18.112619",
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-01-24 11:13:31.158613",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Terms Template",
|
"name": "Payment Terms Template",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@ from frappe.utils import flt
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions,
|
||||||
|
get_dimension_filters)
|
||||||
|
|
||||||
class PeriodClosingVoucher(AccountsController):
|
class PeriodClosingVoucher(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -49,7 +51,15 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
net_pl_balance = 0
|
net_pl_balance = 0
|
||||||
pl_accounts = self.get_pl_balances()
|
dimension_fields = ['t1.cost_center']
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
dimension_fields.append('t1.{0}'.format(dimension))
|
||||||
|
|
||||||
|
dimension_filters, default_dimensions = get_dimension_filters()
|
||||||
|
|
||||||
|
pl_accounts = self.get_pl_balances(dimension_fields)
|
||||||
|
|
||||||
for acc in pl_accounts:
|
for acc in pl_accounts:
|
||||||
if flt(acc.balance_in_company_currency):
|
if flt(acc.balance_in_company_currency):
|
||||||
@@ -65,34 +75,41 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
if flt(acc.balance_in_account_currency) > 0 else 0,
|
if flt(acc.balance_in_account_currency) > 0 else 0,
|
||||||
"credit": abs(flt(acc.balance_in_company_currency)) \
|
"credit": abs(flt(acc.balance_in_company_currency)) \
|
||||||
if flt(acc.balance_in_company_currency) > 0 else 0
|
if flt(acc.balance_in_company_currency) > 0 else 0
|
||||||
}))
|
}, item=acc))
|
||||||
|
|
||||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
net_pl_balance += flt(acc.balance_in_company_currency)
|
||||||
|
|
||||||
if net_pl_balance:
|
if net_pl_balance:
|
||||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entry = self.get_gl_dict({
|
||||||
"account": self.closing_account_head,
|
"account": self.closing_account_head,
|
||||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||||
"cost_center": cost_center
|
"cost_center": cost_center
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
gl_entry.update({
|
||||||
|
dimension: default_dimensions.get(self.company, {}).get(dimension)
|
||||||
|
})
|
||||||
|
|
||||||
|
gl_entries.append(gl_entry)
|
||||||
|
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
|
|
||||||
def get_pl_balances(self):
|
def get_pl_balances(self, dimension_fields):
|
||||||
"""Get balance for pl accounts"""
|
"""Get balance for pl accounts"""
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select
|
select
|
||||||
t1.account, t1.cost_center, t2.account_currency,
|
t1.account, t2.account_currency, {dimension_fields},
|
||||||
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
|
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
|
||||||
sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
|
sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
|
||||||
from `tabGL Entry` t1, `tabAccount` t2
|
from `tabGL Entry` t1, `tabAccount` t2
|
||||||
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
||||||
and t2.docstatus < 2 and t2.company = %s
|
and t2.docstatus < 2 and t2.company = %s
|
||||||
and t1.posting_date between %s and %s
|
and t1.posting_date between %s and %s
|
||||||
group by t1.account, t1.cost_center
|
group by t1.account, {dimension_fields}
|
||||||
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
""".format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ class TestPOSProfile(unittest.TestCase):
|
|||||||
pos_profile = get_pos_profile("_Test Company") or {}
|
pos_profile = get_pos_profile("_Test Company") or {}
|
||||||
if pos_profile:
|
if pos_profile:
|
||||||
doc = frappe.get_doc("POS Profile", pos_profile.get("name"))
|
doc = frappe.get_doc("POS Profile", pos_profile.get("name"))
|
||||||
doc.append('item_groups', {'item_group': '_Test Item Group'})
|
doc.set('item_groups', [{'item_group': '_Test Item Group'}])
|
||||||
doc.append('customer_groups', {'customer_group': '_Test Customer Group'})
|
doc.set('customer_groups', [{'customer_group': '_Test Customer Group'}])
|
||||||
doc.save()
|
doc.save()
|
||||||
items = get_items_list(doc, doc.company)
|
items = get_items_list(doc, doc.company)
|
||||||
customers = get_customers_list(doc)
|
customers = get_customers_list(doc)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class PricingRule(Document):
|
|||||||
self.same_item = 1
|
self.same_item = 1
|
||||||
|
|
||||||
def validate_max_discount(self):
|
def validate_max_discount(self):
|
||||||
if self.rate_or_discount == "Discount Percentage" and self.items:
|
if self.rate_or_discount == "Discount Percentage" and self.get("items"):
|
||||||
for d in self.items:
|
for d in self.items:
|
||||||
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
||||||
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
||||||
@@ -241,6 +241,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
|
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
|
||||||
item_details.update({
|
item_details.update({
|
||||||
'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
|
'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
|
||||||
|
'price_or_product_discount': pricing_rule.price_or_product_discount,
|
||||||
'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
|
'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
|
||||||
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,13 +4,19 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, copy, json
|
|
||||||
from frappe import throw, _
|
import copy
|
||||||
|
import json
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
|
|
||||||
|
import frappe
|
||||||
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
||||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
|
from frappe import _, throw
|
||||||
|
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
|
||||||
|
|
||||||
|
|
||||||
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
||||||
|
|
||||||
@@ -330,9 +336,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
|
|||||||
if pr_doc.mixed_conditions:
|
if pr_doc.mixed_conditions:
|
||||||
amt = args.get('qty') * args.get("price_list_rate")
|
amt = args.get('qty') * args.get("price_list_rate")
|
||||||
if args.get("item_code") != row.get("item_code"):
|
if args.get("item_code") != row.get("item_code"):
|
||||||
amt = row.get('qty') * (row.get("price_list_rate") or args.get("rate"))
|
amt = flt(row.get('qty')) * flt(row.get("price_list_rate") or args.get("rate"))
|
||||||
|
|
||||||
sum_qty += row.get("stock_qty") or args.get("stock_qty") or args.get("qty")
|
sum_qty += flt(row.get("stock_qty")) or flt(args.get("stock_qty")) or flt(args.get("qty"))
|
||||||
sum_amt += amt
|
sum_amt += amt
|
||||||
|
|
||||||
if pr_doc.is_cumulative:
|
if pr_doc.is_cumulative:
|
||||||
@@ -502,18 +508,16 @@ def get_pricing_rule_items(pr_doc):
|
|||||||
return list(set(apply_on_data))
|
return list(set(apply_on_data))
|
||||||
|
|
||||||
def validate_coupon_code(coupon_name):
|
def validate_coupon_code(coupon_name):
|
||||||
from frappe.utils import today,getdate
|
coupon = frappe.get_doc("Coupon Code", coupon_name)
|
||||||
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
|
||||||
if coupon.valid_from:
|
if coupon.valid_from:
|
||||||
if coupon.valid_from > getdate(today()) :
|
if coupon.valid_from > getdate(today()):
|
||||||
frappe.throw(_("Sorry,coupon code validity has not started"))
|
frappe.throw(_("Sorry, this coupon code's validity has not started"))
|
||||||
elif coupon.valid_upto:
|
elif coupon.valid_upto:
|
||||||
if coupon.valid_upto < getdate(today()) :
|
if coupon.valid_upto < getdate(today()):
|
||||||
frappe.throw(_("Sorry,coupon code validity has expired"))
|
frappe.throw(_("Sorry, this coupon code's validity has expired"))
|
||||||
elif coupon.used>=coupon.maximum_use:
|
elif coupon.used >= coupon.maximum_use:
|
||||||
frappe.throw(_("Sorry,coupon code are exhausted"))
|
frappe.throw(_("Sorry, this coupon code is no longer valid"))
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
def update_coupon_code_count(coupon_name,transaction_type):
|
def update_coupon_code_count(coupon_name,transaction_type):
|
||||||
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
||||||
|
|||||||
@@ -261,12 +261,25 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
price_list: this.frm.doc.buying_price_list
|
price_list: this.frm.doc.buying_price_list
|
||||||
}, function() {
|
}, function() {
|
||||||
me.apply_pricing_rule();
|
me.apply_pricing_rule();
|
||||||
|
|
||||||
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
||||||
|
me.frm.doc.tax_withholding_category = me.frm.supplier_tds;
|
||||||
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apply_tds: function(frm) {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
if (!me.frm.doc.apply_tds) {
|
||||||
|
me.frm.set_value("tax_withholding_category", '');
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", 1);
|
||||||
|
} else {
|
||||||
|
me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
credit_to: function() {
|
credit_to: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
if(this.frm.doc.credit_to) {
|
if(this.frm.doc.credit_to) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"is_paid",
|
"is_paid",
|
||||||
"is_return",
|
"is_return",
|
||||||
"apply_tds",
|
"apply_tds",
|
||||||
|
"tax_withholding_category",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"company",
|
"company",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
@@ -73,9 +74,9 @@
|
|||||||
"base_total",
|
"base_total",
|
||||||
"base_net_total",
|
"base_net_total",
|
||||||
"column_break_28",
|
"column_break_28",
|
||||||
|
"total_net_weight",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
"total_net_weight",
|
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"tax_category",
|
"tax_category",
|
||||||
"column_break_49",
|
"column_break_49",
|
||||||
@@ -1284,13 +1285,21 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax_withholding_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Tax Withholding Category",
|
||||||
|
"options": "Tax Withholding Category",
|
||||||
|
"print_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-30 19:13:49.610538",
|
"modified": "2020-04-18 13:05:25.199832",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
@@ -802,7 +802,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -813,7 +813,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"credit_in_account_currency": self.base_paid_amount \
|
"credit_in_account_currency": self.base_paid_amount \
|
||||||
if bank_account_currency==self.company_currency else self.paid_amount,
|
if bank_account_currency==self.company_currency else self.paid_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, bank_account_currency)
|
}, bank_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_write_off_gl_entry(self, gl_entries):
|
def make_write_off_gl_entry(self, gl_entries):
|
||||||
@@ -834,7 +834,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@@ -844,7 +844,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"credit_in_account_currency": self.base_write_off_amount \
|
"credit_in_account_currency": self.base_write_off_amount \
|
||||||
if write_off_account_currency==self.company_currency else self.write_off_amount,
|
if write_off_account_currency==self.company_currency else self.write_off_amount,
|
||||||
"cost_center": self.cost_center or self.write_off_cost_center
|
"cost_center": self.cost_center or self.write_off_cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
@@ -863,8 +863,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"debit_in_account_currency": self.rounding_adjustment,
|
"debit_in_account_currency": self.rounding_adjustment,
|
||||||
"debit": self.base_rounding_adjustment,
|
"debit": self.base_rounding_adjustment,
|
||||||
"cost_center": self.cost_center or round_off_cost_center,
|
"cost_center": self.cost_center or round_off_cost_center,
|
||||||
}
|
}, item=self))
|
||||||
))
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
super(PurchaseInvoice, self).on_cancel()
|
super(PurchaseInvoice, self).on_cancel()
|
||||||
@@ -959,7 +958,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.apply_tds:
|
if not self.apply_tds:
|
||||||
return
|
return
|
||||||
|
|
||||||
tax_withholding_details = get_party_tax_withholding_details(self)
|
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||||
|
|
||||||
if not tax_withholding_details:
|
if not tax_withholding_details:
|
||||||
return
|
return
|
||||||
@@ -982,6 +981,40 @@ class PurchaseInvoice(BuyingController):
|
|||||||
# calculate totals again after applying TDS
|
# calculate totals again after applying TDS
|
||||||
self.calculate_taxes_and_totals()
|
self.calculate_taxes_and_totals()
|
||||||
|
|
||||||
|
def set_status(self, update=False, status=None, update_modified=True):
|
||||||
|
if self.is_new():
|
||||||
|
if self.get('amended_from'):
|
||||||
|
self.status = 'Draft'
|
||||||
|
return
|
||||||
|
|
||||||
|
precision = self.precision("outstanding_amount")
|
||||||
|
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||||
|
due_date = getdate(self.due_date)
|
||||||
|
nowdate = getdate()
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
if self.docstatus == 2:
|
||||||
|
status = "Cancelled"
|
||||||
|
elif self.docstatus == 1:
|
||||||
|
if outstanding_amount > 0 and due_date < nowdate:
|
||||||
|
self.status = "Overdue"
|
||||||
|
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||||
|
self.status = "Unpaid"
|
||||||
|
#Check if outstanding amount is 0 due to debit note issued against invoice
|
||||||
|
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
|
self.status = "Debit Note Issued"
|
||||||
|
elif self.is_return == 1:
|
||||||
|
self.status = "Return"
|
||||||
|
elif outstanding_amount<=0:
|
||||||
|
self.status = "Paid"
|
||||||
|
else:
|
||||||
|
self.status = "Submitted"
|
||||||
|
else:
|
||||||
|
self.status = "Draft"
|
||||||
|
|
||||||
|
if update:
|
||||||
|
self.db_set('status', self.status, update_modified = update_modified)
|
||||||
|
|
||||||
def get_list_context(context=None):
|
def get_list_context(context=None):
|
||||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||||
list_context = get_list_context(context)
|
list_context = get_list_context(context)
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
||||||
|
pi_doc.load_from_db()
|
||||||
|
self.assertTrue(pi_doc.status, "Paid")
|
||||||
|
|
||||||
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
||||||
unlink_payment_on_cancel_of_invoice()
|
unlink_payment_on_cancel_of_invoice()
|
||||||
@@ -203,7 +205,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
pi.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(pi.status, "Unpaid")
|
||||||
self.check_gle_for_pi(pi.name)
|
self.check_gle_for_pi(pi.name)
|
||||||
|
|
||||||
def check_gle_for_pi(self, pi):
|
def check_gle_for_pi(self, pi):
|
||||||
@@ -234,6 +238,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi = frappe.copy_doc(test_records[0])
|
pi = frappe.copy_doc(test_records[0])
|
||||||
pi.insert()
|
pi.insert()
|
||||||
|
pi.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(pi.status, "Draft")
|
||||||
pi.naming_series = 'TEST-'
|
pi.naming_series = 'TEST-'
|
||||||
|
|
||||||
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
||||||
@@ -248,6 +255,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.get("taxes").pop(1)
|
pi.get("taxes").pop(1)
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Unpaid")
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
@@ -599,6 +608,11 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
# return entry
|
# return entry
|
||||||
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
||||||
|
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Debit Note Issued")
|
||||||
|
pi1.load_from_db()
|
||||||
|
self.assertTrue(pi1.status, "Return")
|
||||||
|
|
||||||
actual_qty_2 = get_qty_after_transaction()
|
actual_qty_2 = get_qty_after_transaction()
|
||||||
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
||||||
|
|
||||||
@@ -771,6 +785,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
||||||
|
|
||||||
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Return")
|
||||||
|
|
||||||
outstanding_amount = get_outstanding_amount(pi.doctype,
|
outstanding_amount = get_outstanding_amount(pi.doctype,
|
||||||
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
||||||
|
|||||||
@@ -180,14 +180,16 @@ def get_items_list(pos_profile, company):
|
|||||||
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
||||||
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
|
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
|
||||||
id.expense_account, id.selling_cost_center, id.default_warehouse,
|
id.expense_account, id.selling_cost_center, id.default_warehouse,
|
||||||
i.sales_uom, c.conversion_factor
|
i.sales_uom, c.conversion_factor, it.item_tax_template, it.valid_from
|
||||||
from
|
from
|
||||||
`tabItem` i
|
`tabItem` i
|
||||||
left join `tabItem Default` id on id.parent = i.name and id.company = %s
|
left join `tabItem Default` id on id.parent = i.name and id.company = %s
|
||||||
|
left join `tabItem Tax` it on it.parent = i.name
|
||||||
left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
|
left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
|
||||||
where
|
where
|
||||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
||||||
{cond}
|
{cond}
|
||||||
|
group by i.item_code
|
||||||
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,18 +25,26 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
if(frm.doc.docstatus == 1 && !frm.is_dirty()
|
if(frm.doc.docstatus == 1 && !frm.is_dirty()
|
||||||
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
||||||
|
|
||||||
frm.add_custom_button('e-Way Bill JSON', () => {
|
frm.add_custom_button('E-Way Bill JSON', () => {
|
||||||
var w = window.open(
|
frappe.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
'dt': frm.doc.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
'dn': [frm.doc.name]
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
}, __("Make"));
|
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.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(doclist.doctype)
|
'dt': doclist.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(docnames)
|
'dn': docnames
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
|
data: r.message,
|
||||||
|
docname: docnames
|
||||||
|
};
|
||||||
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
|
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
me.frm.script_manager.trigger("is_pos");
|
me.frm.script_manager.trigger("is_pos");
|
||||||
me.frm.refresh_fields();
|
me.frm.refresh_fields();
|
||||||
}
|
}
|
||||||
|
erpnext.queries.setup_warehouse_query(this.frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(doc, dt, dn) {
|
refresh: function(doc, dt, dn) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-24 19:29:05",
|
"creation": "2013-05-24 19:29:05",
|
||||||
@@ -74,9 +75,9 @@
|
|||||||
"base_total",
|
"base_total",
|
||||||
"base_net_total",
|
"base_net_total",
|
||||||
"column_break_32",
|
"column_break_32",
|
||||||
|
"total_net_weight",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
"total_net_weight",
|
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"taxes_and_charges",
|
"taxes_and_charges",
|
||||||
"column_break_38",
|
"column_break_38",
|
||||||
@@ -148,9 +149,9 @@
|
|||||||
"edit_printing_settings",
|
"edit_printing_settings",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
"group_same_items",
|
"group_same_items",
|
||||||
"language",
|
|
||||||
"column_break_84",
|
|
||||||
"select_print_heading",
|
"select_print_heading",
|
||||||
|
"column_break_84",
|
||||||
|
"language",
|
||||||
"more_information",
|
"more_information",
|
||||||
"inter_company_invoice_reference",
|
"inter_company_invoice_reference",
|
||||||
"customer_group",
|
"customer_group",
|
||||||
@@ -396,7 +397,7 @@
|
|||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "po_no",
|
"fieldname": "po_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Small Text",
|
||||||
"label": "Customer's Purchase Order",
|
"label": "Customer's Purchase Order",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
@@ -1568,7 +1569,8 @@
|
|||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 181,
|
"idx": 181,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2020-02-10 04:57:11.221180",
|
"links": [],
|
||||||
|
"modified": "2020-05-19 17:00:57.208696",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -785,7 +785,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_tax_gl_entries(self, gl_entries):
|
def make_tax_gl_entries(self, gl_entries):
|
||||||
@@ -802,7 +802,7 @@ class SalesInvoice(SellingController):
|
|||||||
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
|
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
|
||||||
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
|
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
|
||||||
"cost_center": tax.cost_center
|
"cost_center": tax.cost_center
|
||||||
}, account_currency)
|
}, account_currency, item=tax)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
@@ -822,7 +822,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
for gle in fixed_asset_gl_entries:
|
for gle in fixed_asset_gl_entries:
|
||||||
gle["against"] = self.customer
|
gle["against"] = self.customer
|
||||||
gl_entries.append(self.get_gl_dict(gle))
|
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||||
|
|
||||||
asset.db_set("disposal_date", self.posting_date)
|
asset.db_set("disposal_date", self.posting_date)
|
||||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||||
@@ -860,7 +860,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@@ -869,7 +869,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against": self.customer,
|
"against": self.customer,
|
||||||
"debit": self.loyalty_amount,
|
"debit": self.loyalty_amount,
|
||||||
"remark": "Loyalty Points redeemed by the customer"
|
"remark": "Loyalty Points redeemed by the customer"
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_pos_gl_entries(self, gl_entries):
|
def make_pos_gl_entries(self, gl_entries):
|
||||||
@@ -890,7 +890,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
payment_mode_account_currency = get_account_currency(payment_mode.account)
|
payment_mode_account_currency = get_account_currency(payment_mode.account)
|
||||||
@@ -903,7 +903,7 @@ class SalesInvoice(SellingController):
|
|||||||
if payment_mode_account_currency==self.company_currency \
|
if payment_mode_account_currency==self.company_currency \
|
||||||
else payment_mode.amount,
|
else payment_mode.amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, payment_mode_account_currency)
|
}, payment_mode_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_change_amount(self, gl_entries):
|
def make_gle_for_change_amount(self, gl_entries):
|
||||||
@@ -921,7 +921,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -930,7 +930,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against": self.customer,
|
"against": self.customer,
|
||||||
"credit": self.base_change_amount,
|
"credit": self.base_change_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Select change amount account"), title="Mandatory Field")
|
frappe.throw(_("Select change amount account"), title="Mandatory Field")
|
||||||
@@ -954,7 +954,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@@ -965,7 +965,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
|
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
|
||||||
else flt(self.write_off_amount, self.precision("write_off_amount"))),
|
else flt(self.write_off_amount, self.precision("write_off_amount"))),
|
||||||
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
|
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
|
||||||
}, write_off_account_currency)
|
}, write_off_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
@@ -982,8 +982,7 @@ class SalesInvoice(SellingController):
|
|||||||
"credit": flt(self.base_rounding_adjustment,
|
"credit": flt(self.base_rounding_adjustment,
|
||||||
self.precision("base_rounding_adjustment")),
|
self.precision("base_rounding_adjustment")),
|
||||||
"cost_center": self.cost_center or round_off_cost_center,
|
"cost_center": self.cost_center or round_off_cost_center,
|
||||||
}
|
}, item=self))
|
||||||
))
|
|
||||||
|
|
||||||
def update_billing_status_in_dn(self, update_modified=True):
|
def update_billing_status_in_dn(self, update_modified=True):
|
||||||
updated_delivery_notes = []
|
updated_delivery_notes = []
|
||||||
|
|||||||
@@ -1834,7 +1834,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
data = get_ewb_data("Sales Invoice", si.name)
|
data = get_ewb_data("Sales Invoice", [si.name])
|
||||||
|
|
||||||
self.assertEqual(data['version'], '1.0.1118')
|
self.assertEqual(data['version'], '1.0.1118')
|
||||||
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
||||||
|
|||||||
@@ -6,23 +6,42 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, getdate
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
class TaxWithholdingCategory(Document):
|
class TaxWithholdingCategory(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_party_tax_withholding_details(ref_doc):
|
def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
|
||||||
tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
|
|
||||||
|
pan_no = ''
|
||||||
|
suppliers = []
|
||||||
|
|
||||||
|
if not tax_withholding_category:
|
||||||
|
tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
|
||||||
|
|
||||||
if not tax_withholding_category:
|
if not tax_withholding_category:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not pan_no:
|
||||||
|
pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
|
||||||
|
|
||||||
|
# Get others suppliers with the same PAN No
|
||||||
|
if pan_no:
|
||||||
|
suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
|
||||||
|
|
||||||
|
if not suppliers:
|
||||||
|
suppliers.append(ref_doc.supplier)
|
||||||
|
|
||||||
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
||||||
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
||||||
if not tax_details:
|
if not tax_details:
|
||||||
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
||||||
.format(tax_withholding_category, ref_doc.company))
|
.format(tax_withholding_category, ref_doc.company))
|
||||||
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
|
||||||
|
tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
|
||||||
|
tax_details, fy, ref_doc.posting_date, pan_no)
|
||||||
|
|
||||||
tax_row = get_tax_row(tax_details, tds_amount)
|
tax_row = get_tax_row(tax_details, tds_amount)
|
||||||
|
|
||||||
return tax_row
|
return tax_row
|
||||||
@@ -51,6 +70,7 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
|||||||
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
||||||
|
|
||||||
def get_tax_row(tax_details, tds_amount):
|
def get_tax_row(tax_details, tds_amount):
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"category": "Total",
|
"category": "Total",
|
||||||
"add_deduct_tax": "Deduct",
|
"add_deduct_tax": "Deduct",
|
||||||
@@ -60,25 +80,36 @@ def get_tax_row(tax_details, tds_amount):
|
|||||||
"tax_amount": tds_amount
|
"tax_amount": tds_amount
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
|
||||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
||||||
tds_amount = 0
|
tds_amount = 0
|
||||||
tds_deducted = 0
|
tds_deducted = 0
|
||||||
|
|
||||||
def _get_tds(amount):
|
def _get_tds(amount, rate):
|
||||||
if amount <= 0:
|
if amount <= 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return amount * tax_details.rate / 100
|
return amount * rate / 100
|
||||||
|
|
||||||
|
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
|
||||||
|
{
|
||||||
|
'pan_no': pan_no,
|
||||||
|
'fiscal_year': fiscal_year
|
||||||
|
}, 'name')
|
||||||
|
ldc = ''
|
||||||
|
|
||||||
|
if ldc_name:
|
||||||
|
ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
|
||||||
|
|
||||||
entries = frappe.db.sql("""
|
entries = frappe.db.sql("""
|
||||||
select voucher_no, credit
|
select voucher_no, credit
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and fiscal_year=%s and credit > 0
|
where company = %s and
|
||||||
""", (ref_doc.supplier, fiscal_year), as_dict=1)
|
party in %s and fiscal_year=%s and credit > 0
|
||||||
|
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
vouchers = [d.voucher_no for d in entries]
|
||||||
advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year)
|
advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
|
||||||
|
|
||||||
tds_vouchers = vouchers + advance_vouchers
|
tds_vouchers = vouchers + advance_vouchers
|
||||||
|
|
||||||
@@ -93,7 +124,20 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
|||||||
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
||||||
|
|
||||||
if tds_deducted:
|
if tds_deducted:
|
||||||
tds_amount = _get_tds(ref_doc.net_total)
|
if ldc:
|
||||||
|
limit_consumed = frappe.db.get_value('Purchase Invoice',
|
||||||
|
{
|
||||||
|
'supplier': ('in', suppliers),
|
||||||
|
'apply_tds': 1,
|
||||||
|
'docstatus': 1
|
||||||
|
}, 'sum(net_total)')
|
||||||
|
|
||||||
|
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
|
||||||
|
ldc.certificate_limit):
|
||||||
|
|
||||||
|
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
|
||||||
|
else:
|
||||||
|
tds_amount = _get_tds(net_total, tax_details.rate)
|
||||||
else:
|
else:
|
||||||
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
||||||
fields = ['sum(net_amount)'],
|
fields = ['sum(net_amount)'],
|
||||||
@@ -106,43 +150,79 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
|||||||
fields = ['sum(credit_in_account_currency)'],
|
fields = ['sum(credit_in_account_currency)'],
|
||||||
filters = {
|
filters = {
|
||||||
'parent': ('in', vouchers), 'docstatus': 1,
|
'parent': ('in', vouchers), 'docstatus': 1,
|
||||||
'party': ref_doc.supplier,
|
'party': ('in', suppliers),
|
||||||
'reference_type': ('not in', ['Purchase Invoice'])
|
'reference_type': ('not in', ['Purchase Invoice'])
|
||||||
}, as_list=1)
|
}, as_list=1)
|
||||||
|
|
||||||
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
||||||
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
||||||
|
|
||||||
supplier_credit_amount += ref_doc.net_total
|
supplier_credit_amount += net_total
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
|
||||||
supplier_credit_amount -= debit_note_amount
|
supplier_credit_amount -= debit_note_amount
|
||||||
|
|
||||||
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
||||||
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
||||||
tds_amount = _get_tds(supplier_credit_amount)
|
|
||||||
|
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
|
||||||
|
ldc.certificate_limit):
|
||||||
|
tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
|
||||||
|
tax_details)
|
||||||
|
else:
|
||||||
|
tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
|
||||||
|
|
||||||
return tds_amount
|
return tds_amount
|
||||||
|
|
||||||
def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=None, to_date=None):
|
def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
|
||||||
condition = "fiscal_year=%s" % fiscal_year
|
condition = "fiscal_year=%s" % fiscal_year
|
||||||
|
|
||||||
|
if company:
|
||||||
|
condition += "and company =%s" % (company)
|
||||||
if from_date and to_date:
|
if from_date and to_date:
|
||||||
condition = "company=%s and posting_date between %s and %s" % (company, from_date, to_date)
|
condition += "and posting_date between %s and %s" % (company, from_date, to_date)
|
||||||
|
|
||||||
|
## Appending the same supplier again if length of suppliers list is 1
|
||||||
|
## since tuple of single element list contains None, For example ('Test Supplier 1', )
|
||||||
|
## and the below query fails
|
||||||
|
if len(suppliers) == 1:
|
||||||
|
suppliers.append(suppliers[0])
|
||||||
|
|
||||||
return frappe.db.sql_list("""
|
return frappe.db.sql_list("""
|
||||||
select distinct voucher_no
|
select distinct voucher_no
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and %s and debit > 0
|
where party in %s and %s and debit > 0
|
||||||
""", (supplier, condition)) or []
|
""", (tuple(suppliers), condition)) or []
|
||||||
|
|
||||||
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
|
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
||||||
condition = ""
|
condition = "and 1=1"
|
||||||
if company:
|
if company:
|
||||||
condition = " and company=%s " % company
|
condition = " and company=%s " % company
|
||||||
|
|
||||||
|
if len(suppliers) == 1:
|
||||||
|
suppliers.append(suppliers[0])
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
return flt(frappe.db.sql("""
|
||||||
select abs(sum(net_total))
|
select abs(sum(net_total))
|
||||||
from `tabPurchase Invoice`
|
from `tabPurchase Invoice`
|
||||||
where supplier=%s %s and is_return=1 and docstatus=1
|
where supplier in %s and is_return=1 and docstatus=1
|
||||||
and posting_date between %s and %s
|
and posting_date between %s and %s %s
|
||||||
""", (supplier, condition, year_start_date, year_end_date)))
|
""", (tuple(suppliers), year_start_date, year_end_date, condition)))
|
||||||
|
|
||||||
|
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
|
||||||
|
if current_amount < (certificate_limit - deducted_amount):
|
||||||
|
return current_amount * rate/100
|
||||||
|
else:
|
||||||
|
ltds_amount = (certificate_limit - deducted_amount)
|
||||||
|
tds_amount = current_amount - ltds_amount
|
||||||
|
|
||||||
|
return ltds_amount * rate/100 + tds_amount * tax_details.rate/100
|
||||||
|
|
||||||
|
def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit):
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
if ((getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and
|
||||||
|
certificate_limit > deducted_amount):
|
||||||
|
valid = True
|
||||||
|
|
||||||
|
return valid
|
||||||
@@ -1458,7 +1458,40 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
|||||||
this.child.batch_no = this.item_batch_no[this.child.item_code];
|
this.child.batch_no = this.item_batch_no[this.child.item_code];
|
||||||
this.child.serial_no = (this.item_serial_no[this.child.item_code]
|
this.child.serial_no = (this.item_serial_no[this.child.item_code]
|
||||||
? this.item_serial_no[this.child.item_code][0] : '');
|
? this.item_serial_no[this.child.item_code][0] : '');
|
||||||
this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]);
|
|
||||||
|
const tax_template_is_valid = true;
|
||||||
|
if (this.items && this.items[0].valid_from) {
|
||||||
|
tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(),
|
||||||
|
this.items[0].valid_from) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : '';
|
||||||
|
this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]);
|
||||||
|
|
||||||
|
if (this.child.item_tax_rate) {
|
||||||
|
this.add_taxes_from_item_tax_template(this.child.item_tax_rate);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
add_taxes_from_item_tax_template: function(item_tax_map) {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
|
||||||
|
if(typeof (item_tax_map) == "string") {
|
||||||
|
item_tax_map = JSON.parse(item_tax_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(item_tax_map, function(tax, rate) {
|
||||||
|
let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
|
||||||
|
if(!found) {
|
||||||
|
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
||||||
|
child.charge_type = "On Net Total";
|
||||||
|
child.account_head = tax;
|
||||||
|
child.description = String(tax);
|
||||||
|
child.rate = rate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update_paid_amount_status: function (update_paid_amount) {
|
update_paid_amount_status: function (update_paid_amount) {
|
||||||
|
|||||||
@@ -468,23 +468,25 @@ def get_timeline_data(doctype, name):
|
|||||||
from frappe.desk.form.load import get_communication_data
|
from frappe.desk.form.load import get_communication_data
|
||||||
|
|
||||||
out = {}
|
out = {}
|
||||||
fields = 'date(creation), count(name)'
|
fields = 'creation, count(*)'
|
||||||
after = add_years(None, -1).strftime('%Y-%m-%d')
|
after = add_years(None, -1).strftime('%Y-%m-%d')
|
||||||
group_by='group by date(creation)'
|
group_by='group by Date(creation)'
|
||||||
|
|
||||||
data = get_communication_data(doctype, name, after=after, group_by='group by date(creation)',
|
data = get_communication_data(doctype, name, after=after, group_by='group by creation',
|
||||||
fields='date(C.creation) as creation, count(C.name)',as_dict=False)
|
fields='C.creation as creation, count(C.name)',as_dict=False)
|
||||||
|
|
||||||
# fetch and append data from Activity Log
|
# fetch and append data from Activity Log
|
||||||
data += frappe.db.sql("""select {fields}
|
data += frappe.db.sql("""select {fields}
|
||||||
from `tabActivity Log`
|
from `tabActivity Log`
|
||||||
where (reference_doctype="{doctype}" and reference_name="{name}")
|
where (reference_doctype=%(doctype)s and reference_name=%(name)s)
|
||||||
or (timeline_doctype in ("{doctype}") and timeline_name="{name}")
|
or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
|
||||||
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}")
|
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
|
||||||
and status!='Success' and creation > {after}
|
and status!='Success' and creation > {after}
|
||||||
{group_by} order by creation desc
|
{group_by} order by creation desc
|
||||||
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
|
""".format(fields=fields, group_by=group_by, after=after), {
|
||||||
group_by=group_by, after=after), as_dict=False)
|
"doctype": doctype,
|
||||||
|
"name": name
|
||||||
|
}, as_dict=False)
|
||||||
|
|
||||||
timeline_items = dict(data)
|
timeline_items = dict(data)
|
||||||
|
|
||||||
@@ -603,10 +605,12 @@ def get_party_shipping_address(doctype, name):
|
|||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def get_partywise_advanced_payment_amount(party_type, posting_date = None):
|
def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None):
|
||||||
cond = "1=1"
|
cond = "1=1"
|
||||||
if posting_date:
|
if posting_date:
|
||||||
cond = "posting_date <= '{0}'".format(posting_date)
|
cond = "posting_date <= '{0}'".format(posting_date)
|
||||||
|
if company:
|
||||||
|
cond += "and company = '{0}'".format(company)
|
||||||
|
|
||||||
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
||||||
FROM `tabGL Entry`
|
FROM `tabGL Entry`
|
||||||
|
|||||||
@@ -344,26 +344,28 @@ class ReceivablePayableReport(object):
|
|||||||
def allocate_outstanding_based_on_payment_terms(self, row):
|
def allocate_outstanding_based_on_payment_terms(self, row):
|
||||||
self.get_payment_terms(row)
|
self.get_payment_terms(row)
|
||||||
for term in row.payment_terms:
|
for term in row.payment_terms:
|
||||||
term.outstanding = term.invoiced
|
|
||||||
|
|
||||||
# update "paid" and "oustanding" for this term
|
# update "paid" and "oustanding" for this term
|
||||||
self.allocate_closing_to_term(row, term, 'paid')
|
if not term.paid:
|
||||||
|
self.allocate_closing_to_term(row, term, 'paid')
|
||||||
|
|
||||||
# update "credit_note" and "oustanding" for this term
|
# update "credit_note" and "oustanding" for this term
|
||||||
if term.outstanding:
|
if term.outstanding:
|
||||||
self.allocate_closing_to_term(row, term, 'credit_note')
|
self.allocate_closing_to_term(row, term, 'credit_note')
|
||||||
|
|
||||||
|
row.payment_terms = sorted(row.payment_terms, key=lambda x: x['due_date'])
|
||||||
|
|
||||||
def get_payment_terms(self, row):
|
def get_payment_terms(self, row):
|
||||||
# build payment_terms for row
|
# build payment_terms for row
|
||||||
payment_terms_details = frappe.db.sql("""
|
payment_terms_details = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
||||||
ps.due_date, ps.payment_amount, ps.description
|
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount
|
||||||
from `tab{0}` si, `tabPayment Schedule` ps
|
from `tab{0}` si, `tabPayment Schedule` ps
|
||||||
where
|
where
|
||||||
si.name = ps.parent and
|
si.name = ps.parent and
|
||||||
si.name = %s
|
si.name = %s
|
||||||
order by ps.due_date
|
order by ps.paid_amount desc, due_date
|
||||||
""".format(row.voucher_type), row.voucher_no, as_dict = 1)
|
""".format(row.voucher_type), row.voucher_no, as_dict = 1)
|
||||||
|
|
||||||
|
|
||||||
@@ -389,11 +391,14 @@ class ReceivablePayableReport(object):
|
|||||||
"invoiced": invoiced,
|
"invoiced": invoiced,
|
||||||
"invoice_grand_total": row.invoiced,
|
"invoice_grand_total": row.invoiced,
|
||||||
"payment_term": d.description,
|
"payment_term": d.description,
|
||||||
"paid": 0.0,
|
"paid": d.paid_amount,
|
||||||
"credit_note": 0.0,
|
"credit_note": 0.0,
|
||||||
"outstanding": 0.0
|
"outstanding": invoiced - d.paid_amount
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if d.paid_amount:
|
||||||
|
row['paid'] -= d.paid_amount
|
||||||
|
|
||||||
def allocate_closing_to_term(self, row, term, key):
|
def allocate_closing_to_term(self, row, term, key):
|
||||||
if row[key]:
|
if row[key]:
|
||||||
if row[key] > term.outstanding:
|
if row[key] > term.outstanding:
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
|||||||
self.get_party_total(args)
|
self.get_party_total(args)
|
||||||
|
|
||||||
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
|
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
|
||||||
self.filters.report_date) or {}
|
self.filters.report_date, self.filters.company) or {}
|
||||||
|
|
||||||
for party, party_dict in iteritems(self.party_total):
|
for party, party_dict in iteritems(self.party_total):
|
||||||
if party_dict.outstanding == 0:
|
if party_dict.outstanding == 0:
|
||||||
|
|||||||
@@ -2,16 +2,19 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import datetime
|
||||||
|
from six import iteritems
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, formatdate
|
||||||
from frappe.utils import formatdate
|
|
||||||
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
||||||
|
|
||||||
from six import iteritems
|
|
||||||
from pprint import pprint
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if not filters: filters = {}
|
if not filters:
|
||||||
|
filters = {}
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
if filters.get("budget_against_filter"):
|
if filters.get("budget_against_filter"):
|
||||||
@@ -43,20 +46,25 @@ def execute(filters=None):
|
|||||||
|
|
||||||
period_data[0] += last_total
|
period_data[0] += last_total
|
||||||
|
|
||||||
if(filters.get("show_cumulative")):
|
if filters.get("show_cumulative"):
|
||||||
last_total = period_data[0] - period_data[1]
|
last_total = period_data[0] - period_data[1]
|
||||||
|
|
||||||
period_data[2] = period_data[0] - period_data[1]
|
period_data[2] = period_data[0] - period_data[1]
|
||||||
row += period_data
|
row += period_data
|
||||||
totals[2] = totals[0] - totals[1]
|
totals[2] = totals[0] - totals[1]
|
||||||
if filters["period"] != "Yearly" :
|
if filters["period"] != "Yearly":
|
||||||
row += totals
|
row += totals
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
|
columns = [
|
||||||
|
_(filters.get("budget_against"))
|
||||||
|
+ ":Link/%s:150" % (filters.get("budget_against")),
|
||||||
|
_("Account") + ":Link/Account:150"
|
||||||
|
]
|
||||||
|
|
||||||
group_months = False if filters["period"] == "Monthly" else True
|
group_months = False if filters["period"] == "Monthly" else True
|
||||||
|
|
||||||
@@ -65,84 +73,181 @@ def get_columns(filters):
|
|||||||
for year in fiscal_year:
|
for year in fiscal_year:
|
||||||
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
|
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
|
||||||
if filters["period"] == "Yearly":
|
if filters["period"] == "Yearly":
|
||||||
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])]
|
labels = [
|
||||||
|
_("Budget") + " " + str(year[0]),
|
||||||
|
_("Actual ") + " " + str(year[0]),
|
||||||
|
_("Variance ") + " " + str(year[0])
|
||||||
|
]
|
||||||
for label in labels:
|
for label in labels:
|
||||||
columns.append(label+":Float:150")
|
columns.append(label + ":Float:150")
|
||||||
else:
|
else:
|
||||||
for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]:
|
for label in [
|
||||||
|
_("Budget") + " (%s)" + " " + str(year[0]),
|
||||||
|
_("Actual") + " (%s)" + " " + str(year[0]),
|
||||||
|
_("Variance") + " (%s)" + " " + str(year[0])
|
||||||
|
]:
|
||||||
if group_months:
|
if group_months:
|
||||||
label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM"))
|
label = label % (
|
||||||
|
formatdate(from_date, format_string="MMM")
|
||||||
|
+ "-"
|
||||||
|
+ formatdate(to_date, format_string="MMM")
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
label = label % formatdate(from_date, format_string="MMM")
|
label = label % formatdate(from_date, format_string="MMM")
|
||||||
|
|
||||||
columns.append(label+":Float:150")
|
columns.append(label + ":Float:150")
|
||||||
|
|
||||||
if filters["period"] != "Yearly" :
|
if filters["period"] != "Yearly":
|
||||||
return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150",
|
return columns + [
|
||||||
_("Total Variance") + ":Float:150"]
|
_("Total Budget") + ":Float:150",
|
||||||
|
_("Total Actual") + ":Float:150",
|
||||||
|
_("Total Variance") + ":Float:150"
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
|
||||||
def get_cost_centers(filters):
|
def get_cost_centers(filters):
|
||||||
cond = "and 1=1"
|
order_by = ""
|
||||||
if filters.get("budget_against") == "Cost Center":
|
if filters.get("budget_against") == "Cost Center":
|
||||||
cond = "order by lft"
|
order_by = "order by lft"
|
||||||
|
|
||||||
if filters.get("budget_against") in ["Cost Center", "Project"]:
|
if filters.get("budget_against") in ["Cost Center", "Project"]:
|
||||||
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
|
return frappe.db.sql_list(
|
||||||
{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
|
"""
|
||||||
|
select
|
||||||
|
name
|
||||||
|
from
|
||||||
|
`tab{tab}`
|
||||||
|
where
|
||||||
|
company = %s
|
||||||
|
{order_by}
|
||||||
|
""".format(tab=filters.get("budget_against"), order_by=order_by),
|
||||||
|
filters.get("company"))
|
||||||
else:
|
else:
|
||||||
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
|
return frappe.db.sql_list(
|
||||||
|
"""
|
||||||
|
select
|
||||||
|
name
|
||||||
|
from
|
||||||
|
`tab{tab}`
|
||||||
|
""".format(tab=filters.get("budget_against"))) # nosec
|
||||||
|
|
||||||
#Get dimension & target details
|
|
||||||
|
# Get dimension & target details
|
||||||
def get_dimension_target_details(filters):
|
def get_dimension_target_details(filters):
|
||||||
|
budget_against = frappe.scrub(filters.get("budget_against"))
|
||||||
cond = ""
|
cond = ""
|
||||||
if filters.get("budget_against_filter"):
|
if filters.get("budget_against_filter"):
|
||||||
cond += " and b.{budget_against} in (%s)".format(budget_against = \
|
cond += """ and b.{budget_against} in (%s)""".format(
|
||||||
frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter')))
|
budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter")))
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql(
|
||||||
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
|
"""
|
||||||
from `tabBudget` b, `tabBudget Account` ba
|
select
|
||||||
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
|
b.{budget_against} as budget_against,
|
||||||
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
|
b.monthly_distribution,
|
||||||
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
ba.account,
|
||||||
tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')),
|
ba.budget_amount,
|
||||||
as_dict=True)
|
b.fiscal_year
|
||||||
|
from
|
||||||
|
`tabBudget` b,
|
||||||
|
`tabBudget Account` ba
|
||||||
|
where
|
||||||
|
b.name = ba.parent
|
||||||
|
and b.docstatus = 1
|
||||||
|
and b.fiscal_year between %s and %s
|
||||||
|
and b.budget_against = %s
|
||||||
|
and b.company = %s
|
||||||
|
{cond}
|
||||||
|
order by
|
||||||
|
b.fiscal_year
|
||||||
|
""".format(
|
||||||
|
budget_against=budget_against,
|
||||||
|
cond=cond,
|
||||||
|
),
|
||||||
|
tuple(
|
||||||
|
[
|
||||||
|
filters.from_fiscal_year,
|
||||||
|
filters.to_fiscal_year,
|
||||||
|
filters.budget_against,
|
||||||
|
filters.company,
|
||||||
|
]
|
||||||
|
+ filters.get("budget_against_filter")
|
||||||
|
), as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
#Get target distribution details of accounts of cost center
|
# Get target distribution details of accounts of cost center
|
||||||
def get_target_distribution_details(filters):
|
def get_target_distribution_details(filters):
|
||||||
target_details = {}
|
target_details = {}
|
||||||
for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation
|
for d in frappe.db.sql(
|
||||||
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
"""
|
||||||
where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
|
select
|
||||||
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
|
md.name,
|
||||||
|
mdp.month,
|
||||||
|
mdp.percentage_allocation
|
||||||
|
from
|
||||||
|
`tabMonthly Distribution Percentage` mdp,
|
||||||
|
`tabMonthly Distribution` md
|
||||||
|
where
|
||||||
|
mdp.parent = md.name
|
||||||
|
and md.fiscal_year between %s and %s
|
||||||
|
order by
|
||||||
|
md.fiscal_year
|
||||||
|
""",
|
||||||
|
(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
|
||||||
|
target_details.setdefault(d.name, {}).setdefault(
|
||||||
|
d.month, flt(d.percentage_allocation)
|
||||||
|
)
|
||||||
|
|
||||||
return target_details
|
return target_details
|
||||||
|
|
||||||
#Get actual details from gl entry
|
# Get actual details from gl entry
|
||||||
def get_actual_details(name, filters):
|
def get_actual_details(name, filters):
|
||||||
cond = "1=1"
|
budget_against = frappe.scrub(filters.get("budget_against"))
|
||||||
budget_against=filters.get("budget_against").replace(" ", "_").lower()
|
cond = ""
|
||||||
|
|
||||||
if filters.get("budget_against") == "Cost Center":
|
if filters.get("budget_against") == "Cost Center":
|
||||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
|
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
|
||||||
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
|
cond = """
|
||||||
|
and lft >= "{lft}"
|
||||||
|
and rgt <= "{rgt}"
|
||||||
|
""".format(lft=cc_lft, rgt=cc_rgt)
|
||||||
|
|
||||||
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
|
ac_details = frappe.db.sql(
|
||||||
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
|
"""
|
||||||
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
|
select
|
||||||
where
|
gl.account,
|
||||||
b.name = ba.parent
|
gl.debit,
|
||||||
and b.docstatus = 1
|
gl.credit,
|
||||||
and ba.account=gl.account
|
gl.fiscal_year,
|
||||||
and b.{budget_against} = gl.{budget_against}
|
MONTHNAME(gl.posting_date) as month_name,
|
||||||
and gl.fiscal_year between %s and %s
|
b.{budget_against} as budget_against
|
||||||
and b.{budget_against}=%s
|
from
|
||||||
and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year
|
`tabGL Entry` gl,
|
||||||
""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year),
|
`tabBudget Account` ba,
|
||||||
(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
|
`tabBudget` b
|
||||||
|
where
|
||||||
|
b.name = ba.parent
|
||||||
|
and b.docstatus = 1
|
||||||
|
and ba.account=gl.account
|
||||||
|
and b.{budget_against} = gl.{budget_against}
|
||||||
|
and gl.fiscal_year between %s and %s
|
||||||
|
and b.{budget_against} = %s
|
||||||
|
and exists(
|
||||||
|
select
|
||||||
|
name
|
||||||
|
from
|
||||||
|
`tab{tab}`
|
||||||
|
where
|
||||||
|
name = gl.{budget_against}
|
||||||
|
{cond}
|
||||||
|
)
|
||||||
|
group by
|
||||||
|
gl.name
|
||||||
|
order by gl.fiscal_year
|
||||||
|
""".format(tab=filters.budget_against, budget_against=budget_against, cond=cond),
|
||||||
|
(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
|
||||||
|
|
||||||
cc_actual_details = {}
|
cc_actual_details = {}
|
||||||
for d in ac_details:
|
for d in ac_details:
|
||||||
@@ -151,7 +256,6 @@ def get_actual_details(name, filters):
|
|||||||
return cc_actual_details
|
return cc_actual_details
|
||||||
|
|
||||||
def get_dimension_account_month_map(filters):
|
def get_dimension_account_month_map(filters):
|
||||||
import datetime
|
|
||||||
dimension_target_details = get_dimension_target_details(filters)
|
dimension_target_details = get_dimension_target_details(filters)
|
||||||
tdd = get_target_distribution_details(filters)
|
tdd = get_target_distribution_details(filters)
|
||||||
|
|
||||||
@@ -161,28 +265,43 @@ def get_dimension_account_month_map(filters):
|
|||||||
actual_details = get_actual_details(ccd.budget_against, filters)
|
actual_details = get_actual_details(ccd.budget_against, filters)
|
||||||
|
|
||||||
for month_id in range(1, 13):
|
for month_id in range(1, 13):
|
||||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
month = datetime.date(2013, month_id, 1).strftime("%B")
|
||||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
|
cam_map.setdefault(ccd.budget_against, {}).setdefault(
|
||||||
.setdefault(month, frappe._dict({
|
ccd.account, {}
|
||||||
"target": 0.0, "actual": 0.0
|
).setdefault(ccd.fiscal_year, {}).setdefault(
|
||||||
}))
|
month, frappe._dict({"target": 0.0, "actual": 0.0})
|
||||||
|
)
|
||||||
|
|
||||||
tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
|
tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
|
||||||
month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \
|
month_percentage = (
|
||||||
if ccd.monthly_distribution else 100.0/12
|
tdd.get(ccd.monthly_distribution, {}).get(month, 0)
|
||||||
|
if ccd.monthly_distribution
|
||||||
|
else 100.0 / 12
|
||||||
|
)
|
||||||
|
|
||||||
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
||||||
|
|
||||||
for ad in actual_details.get(ccd.account, []):
|
for ad in actual_details.get(ccd.account, []):
|
||||||
if ad.month_name == month:
|
if ad.month_name == month and ad.fiscal_year == ccd.fiscal_year:
|
||||||
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
||||||
|
|
||||||
return cam_map
|
return cam_map
|
||||||
|
|
||||||
|
|
||||||
def get_fiscal_years(filters):
|
def get_fiscal_years(filters):
|
||||||
|
|
||||||
fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where
|
fiscal_year = frappe.db.sql(
|
||||||
name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
|
"""
|
||||||
{'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]})
|
select
|
||||||
|
name
|
||||||
|
from
|
||||||
|
`tabFiscal Year`
|
||||||
|
where
|
||||||
|
name between %(from_fiscal_year)s and %(to_fiscal_year)s
|
||||||
|
""",
|
||||||
|
{
|
||||||
|
"from_fiscal_year": filters["from_fiscal_year"],
|
||||||
|
"to_fiscal_year": filters["to_fiscal_year"]
|
||||||
|
})
|
||||||
|
|
||||||
return fiscal_year
|
return fiscal_year
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<h4 class="text-center">
|
<h4 class="text-center">
|
||||||
{% if (filters.party_name) { %}
|
{% if (filters.party_name) { %}
|
||||||
{%= filters.party_name %}
|
{%= filters.party_name %}
|
||||||
{% } else if (filters.party) { %}
|
{% } else if (filters.party && filters.party.length) { %}
|
||||||
{%= filters.party %}
|
{%= filters.party %}
|
||||||
{% } else if (filters.account) { %}
|
{% } else if (filters.account) { %}
|
||||||
{%= filters.account %}
|
{%= filters.account %}
|
||||||
|
|||||||
@@ -293,6 +293,9 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
|||||||
data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
|
data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
|
||||||
data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
|
data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
|
||||||
|
|
||||||
|
if data[key].against_voucher and gle.against_voucher:
|
||||||
|
data[key].against_voucher += ', ' + gle.against_voucher
|
||||||
|
|
||||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
if (gle.posting_date < from_date or
|
if (gle.posting_date < from_date or
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from frappe.utils import flt
|
|||||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||||
filters.periodicity, filters.accumulated_values, filters.company)
|
filters.periodicity, filters.accumulated_values, filters.company)
|
||||||
@@ -27,17 +26,26 @@ def execute(filters=None):
|
|||||||
|
|
||||||
|
|
||||||
gross_income = get_revenue(income, period_list)
|
gross_income = get_revenue(income, period_list)
|
||||||
|
|
||||||
gross_expense = get_revenue(expense, period_list)
|
gross_expense = get_revenue(expense, period_list)
|
||||||
|
|
||||||
if(len(gross_income)==0 and len(gross_expense)== 0):
|
if(len(gross_income)==0 and len(gross_expense)== 0):
|
||||||
data.append({"account_name": "'" + _("Nothing is included in gross") + "'",
|
data.append({
|
||||||
"account": "'" + _("Nothing is included in gross") + "'"})
|
"account_name": "'" + _("Nothing is included in gross") + "'",
|
||||||
|
"account": "'" + _("Nothing is included in gross") + "'"
|
||||||
|
})
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
data.append({"account_name": "'" + _("Included in Gross Profit") + "'",
|
|
||||||
"account": "'" + _("Included in Gross Profit") + "'"})
|
# 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") + "'"
|
||||||
|
})
|
||||||
|
|
||||||
data.append({})
|
data.append({})
|
||||||
data.extend(gross_income or [])
|
data.extend(gross_income or [])
|
||||||
@@ -111,7 +119,6 @@ def set_total(node, value, complete_list, totals):
|
|||||||
|
|
||||||
|
|
||||||
def get_profit(gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False):
|
def get_profit(gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False):
|
||||||
|
|
||||||
profit_loss = {
|
profit_loss = {
|
||||||
"account_name": "'" + _(profit_type) + "'",
|
"account_name": "'" + _(profit_type) + "'",
|
||||||
"account": "'" + _(profit_type) + "'",
|
"account": "'" + _(profit_type) + "'",
|
||||||
@@ -123,7 +130,9 @@ def get_profit(gross_income, gross_expense, period_list, company, profit_type, c
|
|||||||
|
|
||||||
for period in period_list:
|
for period in period_list:
|
||||||
key = period if consolidated else period.key
|
key = period if consolidated else period.key
|
||||||
profit_loss[key] = flt(gross_income[0].get(key, 0)) - flt(gross_expense[0].get(key, 0))
|
gross_income_for_period = flt(gross_income[0].get(key, 0)) if gross_income else 0
|
||||||
|
gross_expense_for_period = flt(gross_expense[0].get(key, 0)) if gross_expense else 0
|
||||||
|
profit_loss[key] = gross_income_for_period - gross_expense_for_period
|
||||||
|
|
||||||
if profit_loss[key]:
|
if profit_loss[key]:
|
||||||
has_value=True
|
has_value=True
|
||||||
@@ -143,8 +152,14 @@ def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expe
|
|||||||
|
|
||||||
for period in period_list:
|
for period in period_list:
|
||||||
key = period if consolidated else period.key
|
key = period if consolidated else period.key
|
||||||
total_income = flt(gross_income[0].get(key, 0)) + flt(non_gross_income[0].get(key, 0))
|
gross_income_for_period = flt(gross_income[0].get(key, 0)) if gross_income else 0
|
||||||
total_expense = flt(gross_expense[0].get(key, 0)) + flt(non_gross_expense[0].get(key, 0))
|
non_gross_income_for_period = flt(non_gross_income[0].get(key, 0)) if non_gross_income else 0
|
||||||
|
|
||||||
|
gross_expense_for_period = flt(gross_expense[0].get(key, 0)) if gross_expense else 0
|
||||||
|
non_gross_expense_for_period = flt(non_gross_expense[0].get(key, 0)) if non_gross_expense else 0
|
||||||
|
|
||||||
|
total_income = gross_income_for_period + non_gross_income_for_period
|
||||||
|
total_expense = gross_expense_for_period + non_gross_expense_for_period
|
||||||
profit_loss[key] = flt(total_income) - flt(total_expense)
|
profit_loss[key] = flt(total_income) - flt(total_expense)
|
||||||
|
|
||||||
if profit_loss[key]:
|
if profit_loss[key]:
|
||||||
|
|||||||
@@ -55,27 +55,27 @@ def get_columns(group_wise_columns, filters):
|
|||||||
columns = []
|
columns = []
|
||||||
column_map = frappe._dict({
|
column_map = frappe._dict({
|
||||||
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
|
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
|
||||||
"posting_date": _("Posting Date") + ":Date",
|
"posting_date": _("Posting Date") + ":Date:100",
|
||||||
"posting_time": _("Posting Time"),
|
"posting_time": _("Posting Time") + ":Data:100",
|
||||||
"item_code": _("Item Code") + ":Link/Item",
|
"item_code": _("Item Code") + ":Link/Item:100",
|
||||||
"item_name": _("Item Name"),
|
"item_name": _("Item Name") + ":Data:100",
|
||||||
"item_group": _("Item Group") + ":Link/Item Group",
|
"item_group": _("Item Group") + ":Link/Item Group:100",
|
||||||
"brand": _("Brand"),
|
"brand": _("Brand") + ":Link/Brand:100",
|
||||||
"description": _("Description"),
|
"description": _("Description") +":Data:100",
|
||||||
"warehouse": _("Warehouse") + ":Link/Warehouse",
|
"warehouse": _("Warehouse") + ":Link/Warehouse:100",
|
||||||
"qty": _("Qty") + ":Float",
|
"qty": _("Qty") + ":Float:80",
|
||||||
"base_rate": _("Avg. Selling Rate") + ":Currency/currency",
|
"base_rate": _("Avg. Selling Rate") + ":Currency/currency:100",
|
||||||
"buying_rate": _("Valuation Rate") + ":Currency/currency",
|
"buying_rate": _("Valuation Rate") + ":Currency/currency:100",
|
||||||
"base_amount": _("Selling Amount") + ":Currency/currency",
|
"base_amount": _("Selling Amount") + ":Currency/currency:100",
|
||||||
"buying_amount": _("Buying Amount") + ":Currency/currency",
|
"buying_amount": _("Buying Amount") + ":Currency/currency:100",
|
||||||
"gross_profit": _("Gross Profit") + ":Currency/currency",
|
"gross_profit": _("Gross Profit") + ":Currency/currency:100",
|
||||||
"gross_profit_percent": _("Gross Profit %") + ":Percent",
|
"gross_profit_percent": _("Gross Profit %") + ":Percent:100",
|
||||||
"project": _("Project") + ":Link/Project",
|
"project": _("Project") + ":Link/Project:100",
|
||||||
"sales_person": _("Sales person"),
|
"sales_person": _("Sales person"),
|
||||||
"allocated_amount": _("Allocated Amount") + ":Currency/currency",
|
"allocated_amount": _("Allocated Amount") + ":Currency/currency:100",
|
||||||
"customer": _("Customer") + ":Link/Customer",
|
"customer": _("Customer") + ":Link/Customer:100",
|
||||||
"customer_group": _("Customer Group") + ":Link/Customer Group",
|
"customer_group": _("Customer Group") + ":Link/Customer Group:100",
|
||||||
"territory": _("Territory") + ":Link/Territory"
|
"territory": _("Territory") + ":Link/Territory:100"
|
||||||
})
|
})
|
||||||
|
|
||||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||||
@@ -85,7 +85,8 @@ def get_columns(group_wise_columns, filters):
|
|||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"label" : _("Currency"),
|
"label" : _("Currency"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Currency"
|
"options": "Currency",
|
||||||
|
"hidden": 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
@@ -277,7 +278,7 @@ class GrossProfitGenerator(object):
|
|||||||
from `tabPurchase Invoice Item` a
|
from `tabPurchase Invoice Item` a
|
||||||
where a.item_code = %s and a.docstatus=1
|
where a.item_code = %s and a.docstatus=1
|
||||||
and modified <= %s
|
and modified <= %s
|
||||||
order by a.modified desc limit 1""", (item_code,self.filters.to_date))
|
order by a.modified desc limit 1""", (item_code, self.filters.to_date))
|
||||||
else:
|
else:
|
||||||
last_purchase_rate = frappe.db.sql("""
|
last_purchase_rate = frappe.db.sql("""
|
||||||
select (a.base_rate / a.conversion_factor)
|
select (a.base_rate / a.conversion_factor)
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
|
|
||||||
data.append(row)
|
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 = total_row_map.get(prev_group_by_value or d.get('item_name'))
|
||||||
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
|
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
|
||||||
data.append(total_row)
|
data.append(total_row)
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
|
|
||||||
data.append(row)
|
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 = total_row_map.get(prev_group_by_value or d.get('item_name'))
|
||||||
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
|
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
|
||||||
data.append(total_row)
|
data.append(total_row)
|
||||||
|
|||||||
@@ -44,9 +44,14 @@ def get_result(filters):
|
|||||||
out = []
|
out = []
|
||||||
for supplier in filters.supplier:
|
for supplier in filters.supplier:
|
||||||
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
||||||
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year][0]
|
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
|
||||||
|
|
||||||
|
if rate:
|
||||||
|
rate = rate[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
||||||
|
|
||||||
except IndexError:
|
except IndexError:
|
||||||
account = []
|
account = []
|
||||||
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
||||||
@@ -76,7 +81,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
|
|||||||
supplier_credit_amount = flt(sum([d.credit for d in entries]))
|
supplier_credit_amount = flt(sum([d.credit for d in entries]))
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
vouchers = [d.voucher_no for d in entries]
|
||||||
vouchers += get_advance_vouchers(supplier, company=company,
|
vouchers += get_advance_vouchers([supplier], company=company,
|
||||||
from_date=from_date, to_date=to_date)
|
from_date=from_date, to_date=to_date)
|
||||||
|
|
||||||
tds_deducted = 0
|
tds_deducted = 0
|
||||||
@@ -89,7 +94,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
|
|||||||
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
||||||
(account, from_date, to_date, company))[0][0])
|
(account, from_date, to_date, company))[0][0])
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount(supplier, from_date, to_date, company=company)
|
debit_note_amount = get_debit_note_amount([supplier], from_date, to_date, company=company)
|
||||||
|
|
||||||
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"default": frappe.defaults.get_user_default("year_end_date"),
|
"default": frappe.defaults.get_user_default("year_end_date"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"cost_center",
|
"fieldname": "cost_center",
|
||||||
"label": __("Cost Center"),
|
"label": __("Cost Center"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
@@ -61,7 +61,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"finance_book",
|
"fieldname": "project",
|
||||||
|
"label": __("Project"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "finance_book",
|
||||||
"label": __("Finance Book"),
|
"label": __("Finance Book"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Finance Book",
|
"options": "Finance Book",
|
||||||
@@ -97,7 +103,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
|
frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
|
||||||
"fieldname": dimension["fieldname"],
|
"fieldname": dimension["fieldname"],
|
||||||
"label": __(dimension["label"]),
|
"label": __(dimension["label"]),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ def get_data(filters):
|
|||||||
gl_entries_by_account = {}
|
gl_entries_by_account = {}
|
||||||
|
|
||||||
opening_balances = get_opening_balances(filters)
|
opening_balances = get_opening_balances(filters)
|
||||||
|
|
||||||
|
#add filter inside list so that the query in financial_statements.py doesn't break
|
||||||
|
if filters.project:
|
||||||
|
filters.project = [filters.project]
|
||||||
|
|
||||||
set_gl_entries_by_account(filters.company, filters.from_date,
|
set_gl_entries_by_account(filters.company, filters.from_date,
|
||||||
filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
|
filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
|
||||||
|
|
||||||
@@ -102,6 +107,9 @@ def get_rootwise_opening_balances(filters, report_type):
|
|||||||
additional_conditions += """ and cost_center in (select name from `tabCost Center`
|
additional_conditions += """ and cost_center in (select name from `tabCost Center`
|
||||||
where lft >= %s and rgt <= %s)""" % (lft, rgt)
|
where lft >= %s and rgt <= %s)""" % (lft, rgt)
|
||||||
|
|
||||||
|
if filters.project:
|
||||||
|
additional_conditions += " and project = %(project)s"
|
||||||
|
|
||||||
if filters.finance_book:
|
if filters.finance_book:
|
||||||
fb_conditions = " AND finance_book = %(finance_book)s"
|
fb_conditions = " AND finance_book = %(finance_book)s"
|
||||||
if filters.include_default_book_entries:
|
if filters.include_default_book_entries:
|
||||||
@@ -116,6 +124,7 @@ def get_rootwise_opening_balances(filters, report_type):
|
|||||||
"from_date": filters.from_date,
|
"from_date": filters.from_date,
|
||||||
"report_type": report_type,
|
"report_type": report_type,
|
||||||
"year_start_date": filters.year_start_date,
|
"year_start_date": filters.year_start_date,
|
||||||
|
"project": filters.project,
|
||||||
"finance_book": filters.finance_book,
|
"finance_book": filters.finance_book,
|
||||||
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
|
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -817,48 +817,37 @@ def create_payment_gateway_account(gateway):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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
|
Renames the document by adding the number as a prefix to the current name and updates
|
||||||
all transaction where it was present.
|
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():
|
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
|
||||||
separator = " - " if " - " in doc_title else " "
|
if docname != new_name:
|
||||||
doc_title = doc_title.split(separator, 1)[1]
|
frappe.rename_doc("Cost Center", docname, new_name, force=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)
|
|
||||||
return new_name
|
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. '''
|
''' Validate if the number entered isn't already assigned to some other document. '''
|
||||||
if number_value:
|
if number_value:
|
||||||
|
filters = {field_name: number_value, "name": ["!=", docname]}
|
||||||
if company:
|
if company:
|
||||||
doctype_with_same_number = frappe.db.get_value(doctype_name,
|
filters["company"] = company
|
||||||
{field_name: number_value, "company": company, "name": ["!=", name]})
|
|
||||||
else:
|
doctype_with_same_number = frappe.db.get_value(doctype_name, filters)
|
||||||
doctype_with_same_number = frappe.db.get_value(doctype_name,
|
|
||||||
{field_name: number_value, "name": ["!=", name]})
|
|
||||||
if doctype_with_same_number:
|
if doctype_with_same_number:
|
||||||
frappe.throw(_("{0} Number {1} already used in account {2}")
|
frappe.throw(_("{0} Number {1} is already used in {2} {3}")
|
||||||
.format(doctype_name, number_value, doctype_with_same_number))
|
.format(doctype_name, number_value, doctype_name.lower(), doctype_with_same_number))
|
||||||
|
|
||||||
def get_autoname_with_number(number_value, doc_title, name, company):
|
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 '-' '''
|
''' append title with prefix as number and suffix as company's abbreviation separated by '-' '''
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Asset(AccountsController):
|
|||||||
self.validate_item()
|
self.validate_item()
|
||||||
self.set_missing_values()
|
self.set_missing_values()
|
||||||
self.prepare_depreciation_data()
|
self.prepare_depreciation_data()
|
||||||
|
self.validate_gross_and_purchase_amount()
|
||||||
if self.get("schedules"):
|
if self.get("schedules"):
|
||||||
self.validate_expected_value_after_useful_life()
|
self.validate_expected_value_after_useful_life()
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ class Asset(AccountsController):
|
|||||||
self.validate_in_use_date()
|
self.validate_in_use_date()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.make_asset_movement()
|
self.make_asset_movement()
|
||||||
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category):
|
if not self.booked_fixed_asset and self.validate_make_gl_entry():
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
def before_cancel(self):
|
def before_cancel(self):
|
||||||
@@ -124,6 +125,12 @@ class Asset(AccountsController):
|
|||||||
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
||||||
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
||||||
|
|
||||||
|
def validate_gross_and_purchase_amount(self):
|
||||||
|
if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount:
|
||||||
|
frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\
|
||||||
|
Please do not book expense of multiple assets against one single Asset.")
|
||||||
|
.format(frappe.bold("equal"), "<br>"), title=_("Invalid Gross Purchase Amount"))
|
||||||
|
|
||||||
def cancel_auto_gen_movement(self):
|
def cancel_auto_gen_movement(self):
|
||||||
movements = frappe.db.sql(
|
movements = frappe.db.sql(
|
||||||
"""SELECT asm.name, asm.docstatus
|
"""SELECT asm.name, asm.docstatus
|
||||||
@@ -448,17 +455,54 @@ class Asset(AccountsController):
|
|||||||
if d.finance_book == self.default_finance_book:
|
if d.finance_book == self.default_finance_book:
|
||||||
return cint(d.idx) - 1
|
return cint(d.idx) - 1
|
||||||
|
|
||||||
|
def validate_make_gl_entry(self):
|
||||||
|
purchase_document = self.get_purchase_document()
|
||||||
|
asset_bought_with_invoice = purchase_document == self.purchase_invoice
|
||||||
|
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
||||||
|
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
||||||
|
# check if expense already has been booked in case of cwip was enabled after purchasing asset
|
||||||
|
expense_booked = False
|
||||||
|
cwip_booked = False
|
||||||
|
|
||||||
|
if asset_bought_with_invoice:
|
||||||
|
expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
||||||
|
(purchase_document, fixed_asset_account), as_dict=1)
|
||||||
|
else:
|
||||||
|
cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
||||||
|
(purchase_document, cwip_account), as_dict=1)
|
||||||
|
|
||||||
|
if cwip_enabled and (expense_booked or not cwip_booked):
|
||||||
|
# if expense has already booked from invoice or cwip is booked from receipt
|
||||||
|
return False
|
||||||
|
elif not cwip_enabled and (not expense_booked or cwip_booked):
|
||||||
|
# if cwip is disabled but expense hasn't been booked yet
|
||||||
|
return True
|
||||||
|
elif cwip_enabled:
|
||||||
|
# default condition
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_purchase_document(self):
|
||||||
|
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
|
||||||
|
purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
|
||||||
|
|
||||||
|
return purchase_document
|
||||||
|
|
||||||
|
def get_asset_accounts(self):
|
||||||
|
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
||||||
|
asset_category = self.asset_category, company = self.company)
|
||||||
|
|
||||||
|
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||||
|
self.name, self.asset_category, self.company)
|
||||||
|
|
||||||
|
return fixed_asset_account, cwip_account
|
||||||
|
|
||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
if ((self.purchase_receipt \
|
purchase_document = self.get_purchase_document()
|
||||||
or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
|
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
||||||
and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
|
||||||
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
|
||||||
asset_category = self.asset_category, company = self.company)
|
|
||||||
|
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
||||||
self.name, self.asset_category, self.company)
|
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": cwip_account,
|
"account": cwip_account,
|
||||||
@@ -468,7 +512,7 @@ class Asset(AccountsController):
|
|||||||
"credit": self.purchase_receipt_amount,
|
"credit": self.purchase_receipt_amount,
|
||||||
"credit_in_account_currency": self.purchase_receipt_amount,
|
"credit_in_account_currency": self.purchase_receipt_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}))
|
}, item=self))
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": fixed_asset_account,
|
"account": fixed_asset_account,
|
||||||
@@ -478,7 +522,7 @@ class Asset(AccountsController):
|
|||||||
"debit": self.purchase_receipt_amount,
|
"debit": self.purchase_receipt_amount,
|
||||||
"debit_in_account_currency": self.purchase_receipt_amount,
|
"debit_in_account_currency": self.purchase_receipt_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}))
|
}, item=self))
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ class TestAsset(unittest.TestCase):
|
|||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
self.assertEquals(doc.items[0].is_fixed_asset, 1)
|
self.assertEquals(doc.items[0].is_fixed_asset, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_schedule_for_straight_line_method(self):
|
def test_schedule_for_straight_line_method(self):
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
qty=1, rate=100000.0, location="Test Location")
|
qty=1, rate=100000.0, location="Test Location")
|
||||||
@@ -564,6 +563,81 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(gle, expected_gle)
|
self.assertEqual(gle, expected_gle)
|
||||||
|
|
||||||
|
def test_gle_with_cwip_toggling(self):
|
||||||
|
# TEST: purchase an asset with cwip enabled and then disable cwip and try submitting the asset
|
||||||
|
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||||
|
|
||||||
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
|
qty=1, rate=5000, do_not_submit=True, location="Test Location")
|
||||||
|
pr.set('taxes', [{
|
||||||
|
'category': 'Total',
|
||||||
|
'add_deduct_tax': 'Add',
|
||||||
|
'charge_type': 'On Net Total',
|
||||||
|
'account_head': '_Test Account Service Tax - _TC',
|
||||||
|
'description': '_Test Account Service Tax',
|
||||||
|
'cost_center': 'Main - _TC',
|
||||||
|
'rate': 5.0
|
||||||
|
}, {
|
||||||
|
'category': 'Valuation and Total',
|
||||||
|
'add_deduct_tax': 'Add',
|
||||||
|
'charge_type': 'On Net Total',
|
||||||
|
'account_head': '_Test Account Shipping Charges - _TC',
|
||||||
|
'description': '_Test Account Shipping Charges',
|
||||||
|
'cost_center': 'Main - _TC',
|
||||||
|
'rate': 5.0
|
||||||
|
}])
|
||||||
|
pr.submit()
|
||||||
|
expected_gle = (
|
||||||
|
("Asset Received But Not Billed - _TC", 0.0, 5250.0),
|
||||||
|
("CWIP Account - _TC", 5250.0, 0.0)
|
||||||
|
)
|
||||||
|
pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
|
where voucher_type='Purchase Receipt' and voucher_no = %s
|
||||||
|
order by account""", pr.name)
|
||||||
|
self.assertEqual(pr_gle, expected_gle)
|
||||||
|
|
||||||
|
pi = make_invoice(pr.name)
|
||||||
|
pi.submit()
|
||||||
|
expected_gle = (
|
||||||
|
("_Test Account Service Tax - _TC", 250.0, 0.0),
|
||||||
|
("_Test Account Shipping Charges - _TC", 250.0, 0.0),
|
||||||
|
("Asset Received But Not Billed - _TC", 5250.0, 0.0),
|
||||||
|
("Creditors - _TC", 0.0, 5500.0),
|
||||||
|
("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
|
||||||
|
)
|
||||||
|
pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
|
where voucher_type='Purchase Invoice' and voucher_no = %s
|
||||||
|
order by account""", pi.name)
|
||||||
|
self.assertEqual(pi_gle, expected_gle)
|
||||||
|
|
||||||
|
asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
||||||
|
asset_doc = frappe.get_doc('Asset', asset)
|
||||||
|
month_end_date = get_last_day(nowdate())
|
||||||
|
asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
|
||||||
|
self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
|
||||||
|
asset_doc.append("finance_books", {
|
||||||
|
"expected_value_after_useful_life": 200,
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"frequency_of_depreciation": 10,
|
||||||
|
"depreciation_start_date": month_end_date
|
||||||
|
})
|
||||||
|
|
||||||
|
# disable cwip and try submitting
|
||||||
|
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
|
||||||
|
asset_doc.submit()
|
||||||
|
# asset should have gl entries even if cwip is disabled
|
||||||
|
expected_gle = (
|
||||||
|
("_Test Fixed Asset - _TC", 5250.0, 0.0),
|
||||||
|
("CWIP Account - _TC", 0.0, 5250.0)
|
||||||
|
)
|
||||||
|
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
|
where voucher_type='Asset' and voucher_no = %s
|
||||||
|
order by account""", asset_doc.name)
|
||||||
|
self.assertEqual(gle, expected_gle)
|
||||||
|
|
||||||
|
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||||
|
|
||||||
def test_expense_head(self):
|
def test_expense_head(self):
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
qty=2, rate=200000.0, location="Test Location")
|
qty=2, rate=200000.0, location="Test Location")
|
||||||
@@ -599,6 +673,7 @@ def create_asset(**args):
|
|||||||
"purchase_date": "2015-01-01",
|
"purchase_date": "2015-01-01",
|
||||||
"calculate_depreciation": 0,
|
"calculate_depreciation": 0,
|
||||||
"gross_purchase_amount": 100000,
|
"gross_purchase_amount": 100000,
|
||||||
|
"purchase_receipt_amount": 100000,
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"available_for_use_date": "2020-06-06",
|
"available_for_use_date": "2020-06-06",
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ from frappe.model.document import Document
|
|||||||
class AssetCategory(Document):
|
class AssetCategory(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_finance_books()
|
self.validate_finance_books()
|
||||||
|
self.validate_account_types()
|
||||||
|
self.validate_account_currency()
|
||||||
|
|
||||||
def validate_finance_books(self):
|
def validate_finance_books(self):
|
||||||
for d in self.finance_books:
|
for d in self.finance_books:
|
||||||
@@ -18,6 +20,46 @@ class AssetCategory(Document):
|
|||||||
if cint(d.get(frappe.scrub(field)))<1:
|
if cint(d.get(frappe.scrub(field)))<1:
|
||||||
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
|
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
|
||||||
|
|
||||||
|
def validate_account_currency(self):
|
||||||
|
account_types = [
|
||||||
|
'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account'
|
||||||
|
]
|
||||||
|
invalid_accounts = []
|
||||||
|
for d in self.accounts:
|
||||||
|
company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency')
|
||||||
|
for type_of_account in account_types:
|
||||||
|
if d.get(type_of_account):
|
||||||
|
account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency")
|
||||||
|
if account_currency != company_currency:
|
||||||
|
invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) }))
|
||||||
|
|
||||||
|
for d in invalid_accounts:
|
||||||
|
frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.")
|
||||||
|
.format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)),
|
||||||
|
title=_("Invalid Account"))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_account_types(self):
|
||||||
|
account_type_map = {
|
||||||
|
'fixed_asset_account': { 'account_type': 'Fixed Asset' },
|
||||||
|
'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' },
|
||||||
|
'depreciation_expense_account': { 'root_type': 'Expense' },
|
||||||
|
'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' }
|
||||||
|
}
|
||||||
|
for d in self.accounts:
|
||||||
|
for fieldname in account_type_map.keys():
|
||||||
|
if d.get(fieldname):
|
||||||
|
selected_account = d.get(fieldname)
|
||||||
|
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
|
||||||
|
selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
|
||||||
|
expected_key_type = account_type_map[fieldname][key_to_match]
|
||||||
|
|
||||||
|
if selected_key_type != expected_key_type:
|
||||||
|
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
|
||||||
|
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
|
||||||
|
title=_("Invalid Account"))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
||||||
if item and frappe.db.get_value("Item", item, "is_fixed_asset"):
|
if item and frappe.db.get_value("Item", item, "is_fixed_asset"):
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class AssetMovement(Document):
|
|||||||
ORDER BY
|
ORDER BY
|
||||||
asm.transaction_date asc
|
asm.transaction_date asc
|
||||||
""", (d.asset, self.company, 'Receipt'), as_dict=1)
|
""", (d.asset, self.company, 'Receipt'), as_dict=1)
|
||||||
|
|
||||||
if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
|
if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
|
||||||
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
|
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
|
||||||
auto generated for Asset {1}').format(self.name, d.asset))
|
auto generated for Asset {1}').format(self.name, d.asset))
|
||||||
|
|||||||
@@ -27,15 +27,6 @@ frappe.ui.form.on("Purchase Order", {
|
|||||||
frm.set_indicator_formatter('item_code',
|
frm.set_indicator_formatter('item_code',
|
||||||
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
|
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
|
||||||
|
|
||||||
frm.set_query("blanket_order", "items", function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"docstatus": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("expense_account", "items", function() {
|
frm.set_query("expense_account", "items", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_expense_account",
|
query: "erpnext.controllers.queries.get_expense_account",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-21 16:16:39",
|
"creation": "2013-05-21 16:16:39",
|
||||||
@@ -63,9 +64,9 @@
|
|||||||
"base_total",
|
"base_total",
|
||||||
"base_net_total",
|
"base_net_total",
|
||||||
"column_break_26",
|
"column_break_26",
|
||||||
|
"total_net_weight",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
"total_net_weight",
|
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"tax_category",
|
"tax_category",
|
||||||
"column_break_50",
|
"column_break_50",
|
||||||
@@ -171,6 +172,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))",
|
"depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))",
|
||||||
|
"description": "Fetch items based on Default Supplier.",
|
||||||
"fieldname": "get_items_from_open_material_requests",
|
"fieldname": "get_items_from_open_material_requests",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Get Items from Open Material Requests"
|
"label": "Get Items from Open Material Requests"
|
||||||
@@ -1053,7 +1055,8 @@
|
|||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2020-01-14 18:54:39.694448",
|
"links": [],
|
||||||
|
"modified": "2020-04-17 13:04:28.185197",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@@ -141,19 +141,18 @@ def get_conditions(filters):
|
|||||||
conditions = ""
|
conditions = ""
|
||||||
|
|
||||||
if filters.get("company"):
|
if filters.get("company"):
|
||||||
conditions += " AND company=%s"% frappe.db.escape(filters.get('company'))
|
conditions += " AND par.company=%s" % frappe.db.escape(filters.get('company'))
|
||||||
|
|
||||||
if filters.get("cost_center") or filters.get("project"):
|
if filters.get("cost_center") or filters.get("project"):
|
||||||
conditions += """
|
conditions += """
|
||||||
AND (cost_center=%s
|
AND (child.`cost_center`=%s OR child.`project`=%s)
|
||||||
OR project=%s)
|
""" % (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
|
||||||
"""% (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
|
|
||||||
|
|
||||||
if filters.get("from_date"):
|
if filters.get("from_date"):
|
||||||
conditions += " AND transaction_date>=%s"% filters.get('from_date')
|
conditions += " AND par.transaction_date>='%s'" % filters.get('from_date')
|
||||||
|
|
||||||
if filters.get("to_date"):
|
if filters.get("to_date"):
|
||||||
conditions += " AND transaction_date<=%s"% filters.get('to_date')
|
conditions += " AND par.transaction_date<='%s'" % filters.get('to_date')
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
@@ -162,7 +161,6 @@ def get_data(filters):
|
|||||||
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
|
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
|
||||||
pr_records = get_mapped_pr_records()
|
pr_records = get_mapped_pr_records()
|
||||||
pi_records = get_mapped_pi_records()
|
pi_records = get_mapped_pi_records()
|
||||||
print(pi_records)
|
|
||||||
|
|
||||||
procurement_record=[]
|
procurement_record=[]
|
||||||
if procurement_record_against_mr:
|
if procurement_record_against_mr:
|
||||||
@@ -198,16 +196,16 @@ def get_mapped_mr_details(conditions):
|
|||||||
mr_records = {}
|
mr_records = {}
|
||||||
mr_details = frappe.db.sql("""
|
mr_details = frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
mr.transaction_date,
|
par.transaction_date,
|
||||||
mr.per_ordered,
|
par.per_ordered,
|
||||||
mr_item.name,
|
child.name,
|
||||||
mr_item.parent,
|
child.parent,
|
||||||
mr_item.amount
|
child.amount
|
||||||
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
FROM `tabMaterial Request` par, `tabMaterial Request Item` child
|
||||||
WHERE
|
WHERE
|
||||||
mr.per_ordered>=0
|
par.per_ordered>=0
|
||||||
AND mr.name=mr_item.parent
|
AND par.name=child.parent
|
||||||
AND mr.docstatus=1
|
AND par.docstatus=1
|
||||||
{conditions}
|
{conditions}
|
||||||
""".format(conditions=conditions), as_dict=1) #nosec
|
""".format(conditions=conditions), as_dict=1) #nosec
|
||||||
|
|
||||||
@@ -254,29 +252,29 @@ def get_mapped_pr_records():
|
|||||||
def get_po_entries(conditions):
|
def get_po_entries(conditions):
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
po_item.name,
|
child.name,
|
||||||
po_item.parent,
|
child.parent,
|
||||||
po_item.cost_center,
|
child.cost_center,
|
||||||
po_item.project,
|
child.project,
|
||||||
po_item.warehouse,
|
child.warehouse,
|
||||||
po_item.material_request,
|
child.material_request,
|
||||||
po_item.material_request_item,
|
child.material_request_item,
|
||||||
po_item.description,
|
child.description,
|
||||||
po_item.stock_uom,
|
child.stock_uom,
|
||||||
po_item.qty,
|
child.qty,
|
||||||
po_item.amount,
|
child.amount,
|
||||||
po_item.base_amount,
|
child.base_amount,
|
||||||
po_item.schedule_date,
|
child.schedule_date,
|
||||||
po.transaction_date,
|
par.transaction_date,
|
||||||
po.supplier,
|
par.supplier,
|
||||||
po.status,
|
par.status,
|
||||||
po.owner
|
par.owner
|
||||||
FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item
|
FROM `tabPurchase Order` par, `tabPurchase Order Item` child
|
||||||
WHERE
|
WHERE
|
||||||
po.docstatus = 1
|
par.docstatus = 1
|
||||||
AND po.name = po_item.parent
|
AND par.name = child.parent
|
||||||
AND po.status not in ("Closed","Completed","Cancelled")
|
AND par.status not in ("Closed","Completed","Cancelled")
|
||||||
{conditions}
|
{conditions}
|
||||||
GROUP BY
|
GROUP BY
|
||||||
po.name,po_item.item_code
|
par.name, child.item_code
|
||||||
""".format(conditions=conditions), as_dict=1) #nosec
|
""".format(conditions=conditions), as_dict=1) #nosec
|
||||||
43
erpnext/change_log/v12/v12_9_0.md
Normal file
43
erpnext/change_log/v12/v12_9_0.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
## ERPNext v12.9.0 Release Note
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
- Pick List enhancements [#20962](https://github.com/frappe/erpnext/pull/20962)
|
||||||
|
- The purpose **Delivery Against Sales Order** has been changed to **Delivery** and patch for existing records.
|
||||||
|
- If the purpose is Delivery then allow rows without Sales Order against them to be mapped to the Delivery Note.
|
||||||
|
- **Update Current Stock** button to update locations and quantity after Submit.
|
||||||
|
- Validations if Item Locations table is empty (on creation of Delivery Note, Stock Entry).
|
||||||
|
- Company-wise fetching of locations and quantity.
|
||||||
|
- Payment allocation on Payment Entry based on invoice payment terms. [#20945](https://github.com/frappe/erpnext/pull/20945)
|
||||||
|
- Allow Tax Withholding Category selection at invoice level [#20870](https://github.com/frappe/erpnext/pull/20870)
|
||||||
|
- Enhanced Employee Leave Balance report, added new fields New Allocation, Expired Leaves. [#21282](https://github.com/frappe/erpnext/pull/21282)
|
||||||
|
- Provision to set Default Item Manufacturer. [#21197](https://github.com/frappe/erpnext/pull/21197)
|
||||||
|
- Tax Amount in Credit Note print format should be shown in positive. [#21252](https://github.com/frappe/erpnext/pull/21252)
|
||||||
|
- On creation of return from employee advance, sets default voucher type as Bank Entry and default debit account as Cash. [#21411](https://github.com/frappe/erpnext/pull/21411)
|
||||||
|
- On saving a Contact linked with a Lead, update the contact info from Contact into the Lead. [#21469](https://github.com/frappe/erpnext/pull/21469)
|
||||||
|
- Warning on making payment against paid invoices. [#21501](https://github.com/frappe/erpnext/pull/21501)
|
||||||
|
- Enhanced Stock Balance report with color-coding. [#21516](https://github.com/frappe/erpnext/pull/21516)
|
||||||
|
- Added total row in sales analytics report [#21519](https://github.com/frappe/erpnext/pull/21519)
|
||||||
|
- Asset related accounts must be in company currency. [#21524](https://github.com/frappe/erpnext/pull/21524)
|
||||||
|
- Accounting Dimensions in Period Closing Voucher. [#21564](https://github.com/frappe/erpnext/pull/21564)
|
||||||
|
- Renamed LMS to Learning Management System. [#21645](https://github.com/frappe/erpnext/pull/21645)
|
||||||
|
- Allow half-day attendance only via leave application. [#21719](https://github.com/frappe/erpnext/pull/21719)
|
||||||
|
- In BOM, allowed Price List in other than company currency. [#21585](https://github.com/frappe/erpnext/pull/21585)
|
||||||
|
|
||||||
|
### Fixes:
|
||||||
|
- Account Type validation for accounts selected in Asset Category. [#21102](https://github.com/frappe/erpnext/pull/21102)
|
||||||
|
- Warehouse unset if an item doesn't have a default warehouse. [#21285](https://github.com/frappe/erpnext/pull/21285)
|
||||||
|
- Bin Requested Qty should be calculated for customer-provided items. [#21300](https://github.com/frappe/erpnext/pull/21300)
|
||||||
|
- On change of item in sales and purchase transactions, UOM should be reset based on the item's default UOM. [#21254](https://github.com/frappe/erpnext/pull/21254)
|
||||||
|
- Target warehouse in Delivery Note and Sales Invoice should not be set based on user permission. Patch to fix affected records. [#21359](https://github.com/frappe/erpnext/pull/21359)
|
||||||
|
- Budget validation against an Accounting Dimension was missing. [#21268](https://github.com/frappe/erpnext/pull/21268)
|
||||||
|
- On change of qty in Stock Entry, the fields in base currency were not set for non-serialized items. [#21389](https://github.com/frappe/erpnext/pull/21389)
|
||||||
|
- Payment request not able to make against fees [#21486](https://github.com/frappe/erpnext/pull/21486)
|
||||||
|
- Cost Center renaming is allowed only from Cost Center form [#21503](https://github.com/frappe/erpnext/pull/21503)
|
||||||
|
- Accounts Payable report showing the advance amount of other company [#21548](https://github.com/frappe/erpnext/pull/21548)
|
||||||
|
- Heatmap was not working for customer and supplier [#21578](https://github.com/frappe/erpnext/pull/21578)
|
||||||
|
- Item tax template set in the Item master is not fetched into the sales invoice and taxes are not shown in the offline pos. [#21714](https://github.com/frappe/erpnext/pull/21714)
|
||||||
|
- Validate duplicate creation of expiry ledger entry for carry forward allocation and patch to remove duplicate ledgers. [#21505](https://github.com/frappe/erpnext/pull/21505)
|
||||||
|
- Dimensions were not getting added for some GL Entries and were missing in some recently added transactions. [#21689](https://github.com/frappe/erpnext/pull/21689)
|
||||||
|
- In Repack entry, set rate for finished goods based on the cost of raw materials. [#21736](https://github.com/frappe/erpnext/pull/21736)
|
||||||
|
- Removed Guest access from Lead. [#21692](https://github.com/frappe/erpnext/pull/21692)
|
||||||
|
- Standard and Custom queries did not show search fields for Link fields. [#21685](https://github.com/frappe/erpnext/pull/21685)
|
||||||
@@ -819,7 +819,7 @@ class AccountsController(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
for d in self.get("payment_schedule"):
|
for d in self.get("payment_schedule"):
|
||||||
if d.invoice_portion:
|
if d.invoice_portion:
|
||||||
d.payment_amount = grand_total * flt(d.invoice_portion) / 100
|
d.payment_amount = flt(grand_total * flt(d.invoice_portion) / 100, d.precision('payment_amount'))
|
||||||
|
|
||||||
def set_due_date(self):
|
def set_due_date(self):
|
||||||
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
||||||
@@ -834,7 +834,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
for d in self.get("payment_schedule"):
|
for d in self.get("payment_schedule"):
|
||||||
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
|
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
|
||||||
frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
|
frappe.throw(_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(d.idx))
|
||||||
elif d.due_date in dates:
|
elif d.due_date in dates:
|
||||||
li.append(_("{0} in row {1}").format(d.due_date, d.idx))
|
li.append(_("{0} in row {1}").format(d.due_date, d.idx))
|
||||||
dates.append(d.due_date)
|
dates.append(d.due_date)
|
||||||
|
|||||||
@@ -666,7 +666,7 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
if len(created_assets) > 5:
|
if len(created_assets) > 5:
|
||||||
# dont show asset form links if more than 5 assets are created
|
# dont show asset form links if more than 5 assets are created
|
||||||
messages.append(_('{} Asset{} created for {}').format(len(created_assets), is_plural, frappe.bold(d.item_code)))
|
messages.append(_('{} Assets created for {}').format(len(created_assets), frappe.bold(d.item_code)))
|
||||||
else:
|
else:
|
||||||
assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets))
|
assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets))
|
||||||
assets_link = frappe.bold(','.join(assets_link))
|
assets_link = frappe.bold(','.join(assets_link))
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ from frappe.desk.reportview import get_match_cond, get_filters_cond
|
|||||||
from frappe.utils import nowdate, getdate
|
from frappe.utils import nowdate, getdate
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from erpnext.stock.get_item_details import _get_item_tax_template
|
from erpnext.stock.get_item_details import _get_item_tax_template
|
||||||
|
from frappe.utils import unique
|
||||||
|
|
||||||
# searches for active employees
|
# searches for active employees
|
||||||
def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
conditions = []
|
conditions = []
|
||||||
return frappe.db.sql("""select name, employee_name from `tabEmployee`
|
fields = get_fields("Employee", ["name", "employee_name"])
|
||||||
|
|
||||||
|
return frappe.db.sql("""select {fields} from `tabEmployee`
|
||||||
where status = 'Active'
|
where status = 'Active'
|
||||||
and docstatus < 2
|
and docstatus < 2
|
||||||
and ({key} like %(txt)s
|
and ({key} like %(txt)s
|
||||||
@@ -24,6 +27,7 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
idx desc,
|
idx desc,
|
||||||
name, employee_name
|
name, employee_name
|
||||||
limit %(start)s, %(page_len)s""".format(**{
|
limit %(start)s, %(page_len)s""".format(**{
|
||||||
|
'fields': ", ".join(fields),
|
||||||
'key': searchfield,
|
'key': searchfield,
|
||||||
'fcond': get_filters_cond(doctype, filters, conditions),
|
'fcond': get_filters_cond(doctype, filters, conditions),
|
||||||
'mcond': get_match_cond(doctype)
|
'mcond': get_match_cond(doctype)
|
||||||
@@ -34,9 +38,12 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
'page_len': page_len
|
'page_len': page_len
|
||||||
})
|
})
|
||||||
|
|
||||||
# searches for leads which are not converted
|
|
||||||
|
# searches for leads which are not converted
|
||||||
def lead_query(doctype, txt, searchfield, start, page_len, filters):
|
def lead_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
return frappe.db.sql("""select name, lead_name, company_name from `tabLead`
|
fields = get_fields("Lead", ["name", "lead_name", "company_name"])
|
||||||
|
|
||||||
|
return frappe.db.sql("""select {fields} from `tabLead`
|
||||||
where docstatus < 2
|
where docstatus < 2
|
||||||
and ifnull(status, '') != 'Converted'
|
and ifnull(status, '') != 'Converted'
|
||||||
and ({key} like %(txt)s
|
and ({key} like %(txt)s
|
||||||
@@ -50,6 +57,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
idx desc,
|
idx desc,
|
||||||
name, lead_name
|
name, lead_name
|
||||||
limit %(start)s, %(page_len)s""".format(**{
|
limit %(start)s, %(page_len)s""".format(**{
|
||||||
|
'fields': ", ".join(fields),
|
||||||
'key': searchfield,
|
'key': searchfield,
|
||||||
'mcond':get_match_cond(doctype)
|
'mcond':get_match_cond(doctype)
|
||||||
}), {
|
}), {
|
||||||
@@ -59,6 +67,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
'page_len': page_len
|
'page_len': page_len
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# searches for customer
|
# searches for customer
|
||||||
def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
conditions = []
|
conditions = []
|
||||||
@@ -69,13 +78,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
else:
|
else:
|
||||||
fields = ["name", "customer_name", "customer_group", "territory"]
|
fields = ["name", "customer_name", "customer_group", "territory"]
|
||||||
|
|
||||||
meta = frappe.get_meta("Customer")
|
fields = get_fields("Customer", fields)
|
||||||
searchfields = meta.get_search_fields()
|
|
||||||
searchfields = searchfields + [f for f in [searchfield or "name", "customer_name"] \
|
|
||||||
if not f in searchfields]
|
|
||||||
fields = fields + [f for f in searchfields if not f in fields]
|
|
||||||
|
|
||||||
fields = ", ".join(fields)
|
searchfields = frappe.get_meta("Customer").get_search_fields()
|
||||||
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
|
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
|
||||||
|
|
||||||
return frappe.db.sql("""select {fields} from `tabCustomer`
|
return frappe.db.sql("""select {fields} from `tabCustomer`
|
||||||
@@ -88,7 +93,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
idx desc,
|
idx desc,
|
||||||
name, customer_name
|
name, customer_name
|
||||||
limit %(start)s, %(page_len)s""".format(**{
|
limit %(start)s, %(page_len)s""".format(**{
|
||||||
"fields": fields,
|
"fields": ", ".join(fields),
|
||||||
"scond": searchfields,
|
"scond": searchfields,
|
||||||
"mcond": get_match_cond(doctype),
|
"mcond": get_match_cond(doctype),
|
||||||
"fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
"fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||||
@@ -99,6 +104,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
'page_len': page_len
|
'page_len': page_len
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# searches for supplier
|
# searches for supplier
|
||||||
def supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
def supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
supp_master_name = frappe.defaults.get_user_default("supp_master_name")
|
supp_master_name = frappe.defaults.get_user_default("supp_master_name")
|
||||||
@@ -106,7 +112,8 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
fields = ["name", "supplier_group"]
|
fields = ["name", "supplier_group"]
|
||||||
else:
|
else:
|
||||||
fields = ["name", "supplier_name", "supplier_group"]
|
fields = ["name", "supplier_name", "supplier_group"]
|
||||||
fields = ", ".join(fields)
|
|
||||||
|
fields = get_fields("Supplier", fields)
|
||||||
|
|
||||||
return frappe.db.sql("""select {field} from `tabSupplier`
|
return frappe.db.sql("""select {field} from `tabSupplier`
|
||||||
where docstatus < 2
|
where docstatus < 2
|
||||||
@@ -119,7 +126,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
idx desc,
|
idx desc,
|
||||||
name, supplier_name
|
name, supplier_name
|
||||||
limit %(start)s, %(page_len)s """.format(**{
|
limit %(start)s, %(page_len)s """.format(**{
|
||||||
'field': fields,
|
'field': ', '.join(fields),
|
||||||
'key': searchfield,
|
'key': searchfield,
|
||||||
'mcond':get_match_cond(doctype)
|
'mcond':get_match_cond(doctype)
|
||||||
}), {
|
}), {
|
||||||
@@ -129,6 +136,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
'page_len': page_len
|
'page_len': page_len
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
company_currency = erpnext.get_company_currency(filters.get('company'))
|
company_currency = erpnext.get_company_currency(filters.get('company'))
|
||||||
|
|
||||||
@@ -153,6 +161,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
return tax_accounts
|
return tax_accounts
|
||||||
|
|
||||||
|
|
||||||
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
||||||
conditions = []
|
conditions = []
|
||||||
|
|
||||||
@@ -214,10 +223,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
|||||||
"page_len": page_len
|
"page_len": page_len
|
||||||
}, as_dict=as_dict)
|
}, as_dict=as_dict)
|
||||||
|
|
||||||
|
|
||||||
def bom(doctype, txt, searchfield, start, page_len, filters):
|
def bom(doctype, txt, searchfield, start, page_len, filters):
|
||||||
conditions = []
|
conditions = []
|
||||||
|
fields = get_fields("BOM", ["name", "item"])
|
||||||
|
|
||||||
return frappe.db.sql("""select tabBOM.name, tabBOM.item
|
return frappe.db.sql("""select {fields}
|
||||||
from tabBOM
|
from tabBOM
|
||||||
where tabBOM.docstatus=1
|
where tabBOM.docstatus=1
|
||||||
and tabBOM.is_active=1
|
and tabBOM.is_active=1
|
||||||
@@ -227,6 +238,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||||
idx desc, name
|
idx desc, name
|
||||||
limit %(start)s, %(page_len)s """.format(
|
limit %(start)s, %(page_len)s """.format(
|
||||||
|
fields=", ".join(fields),
|
||||||
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||||
mcond=get_match_cond(doctype).replace('%', '%%'),
|
mcond=get_match_cond(doctype).replace('%', '%%'),
|
||||||
key=searchfield),
|
key=searchfield),
|
||||||
@@ -237,13 +249,16 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
'page_len': page_len or 20
|
'page_len': page_len or 20
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
||||||
cond = ''
|
cond = ''
|
||||||
if filters.get('customer'):
|
if filters.get('customer'):
|
||||||
cond = """(`tabProject`.customer = %s or
|
cond = """(`tabProject`.customer = %s or
|
||||||
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
|
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
|
||||||
|
|
||||||
return frappe.db.sql("""select `tabProject`.name from `tabProject`
|
fields = get_fields("Project", ["name"])
|
||||||
|
|
||||||
|
return frappe.db.sql("""select {fields} from `tabProject`
|
||||||
where `tabProject`.status not in ("Completed", "Cancelled")
|
where `tabProject`.status not in ("Completed", "Cancelled")
|
||||||
and {cond} `tabProject`.name like %(txt)s {match_cond}
|
and {cond} `tabProject`.name like %(txt)s {match_cond}
|
||||||
order by
|
order by
|
||||||
@@ -251,6 +266,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
idx desc,
|
idx desc,
|
||||||
`tabProject`.name asc
|
`tabProject`.name asc
|
||||||
limit {start}, {page_len}""".format(
|
limit {start}, {page_len}""".format(
|
||||||
|
fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]),
|
||||||
cond=cond,
|
cond=cond,
|
||||||
match_cond=get_match_cond(doctype),
|
match_cond=get_match_cond(doctype),
|
||||||
start=start,
|
start=start,
|
||||||
@@ -261,8 +277,10 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
|
|
||||||
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
|
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
|
||||||
|
fields = get_fields("Delivery Note", ["name", "customer", "posting_date"])
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date
|
select %(fields)s
|
||||||
from `tabDelivery Note`
|
from `tabDelivery Note`
|
||||||
where `tabDelivery Note`.`%(key)s` like %(txt)s and
|
where `tabDelivery Note`.`%(key)s` like %(txt)s and
|
||||||
`tabDelivery Note`.docstatus = 1
|
`tabDelivery Note`.docstatus = 1
|
||||||
@@ -277,6 +295,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
|||||||
)
|
)
|
||||||
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s
|
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s
|
||||||
""" % {
|
""" % {
|
||||||
|
"fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]),
|
||||||
"key": searchfield,
|
"key": searchfield,
|
||||||
"fcond": get_filters_cond(doctype, filters, []),
|
"fcond": get_filters_cond(doctype, filters, []),
|
||||||
"mcond": get_match_cond(doctype),
|
"mcond": get_match_cond(doctype),
|
||||||
@@ -342,6 +361,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
order by expiry_date, name desc
|
order by expiry_date, name desc
|
||||||
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
|
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
|
||||||
|
|
||||||
|
|
||||||
def get_account_list(doctype, txt, searchfield, start, page_len, filters):
|
def get_account_list(doctype, txt, searchfield, start, page_len, filters):
|
||||||
filter_list = []
|
filter_list = []
|
||||||
|
|
||||||
@@ -365,6 +385,21 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
limit_start=start, limit_page_length=page_len, as_list=True)
|
limit_start=start, limit_page_length=page_len, as_list=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
|
||||||
|
return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date
|
||||||
|
from `tabBlanket Order` bo, `tabBlanket Order Item` boi
|
||||||
|
where
|
||||||
|
boi.parent = bo.name
|
||||||
|
and boi.item_code = {item_code}
|
||||||
|
and bo.blanket_order_type = '{blanket_order_type}'
|
||||||
|
and bo.company = {company}
|
||||||
|
and bo.docstatus = 1"""
|
||||||
|
.format(item_code = frappe.db.escape(filters.get("item")),
|
||||||
|
blanket_order_type = filters.get("blanket_order_type"),
|
||||||
|
company = frappe.db.escape(filters.get("company"))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
||||||
from erpnext.controllers.queries import get_match_cond
|
from erpnext.controllers.queries import get_match_cond
|
||||||
@@ -470,6 +505,7 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
return frappe.db.sql(query, filters)
|
return frappe.db.sql(query, filters)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters):
|
def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
item_filters = [
|
item_filters = [
|
||||||
@@ -487,6 +523,7 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters)
|
|||||||
)
|
)
|
||||||
return item_manufacturers
|
return item_manufacturers
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
|
def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
|
||||||
query = """
|
query = """
|
||||||
@@ -500,6 +537,7 @@ def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
return frappe.db.sql(query, filters)
|
return frappe.db.sql(query, filters)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
|
def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
|
||||||
query = """
|
query = """
|
||||||
@@ -513,6 +551,7 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
return frappe.db.sql(query, filters)
|
return frappe.db.sql(query, filters)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
|
def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
|
||||||
|
|
||||||
@@ -536,3 +575,13 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
taxes = _get_item_tax_template(args, taxes, for_validate=True)
|
taxes = _get_item_tax_template(args, taxes, for_validate=True)
|
||||||
return [(d,) for d in set(taxes)]
|
return [(d,) for d in set(taxes)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_fields(doctype, fields=[]):
|
||||||
|
meta = frappe.get_meta(doctype)
|
||||||
|
fields.extend(meta.get_search_fields())
|
||||||
|
|
||||||
|
if meta.title_field and not meta.title_field.strip() in fields:
|
||||||
|
fields.insert(1, meta.title_field.strip())
|
||||||
|
|
||||||
|
return unique(fields)
|
||||||
|
|||||||
@@ -165,9 +165,9 @@ class SellingController(StockController):
|
|||||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
def validate_selling_price(self):
|
def validate_selling_price(self):
|
||||||
def throw_message(item_name, rate, ref_rate_field):
|
def throw_message(idx, item_name, rate, ref_rate_field):
|
||||||
frappe.throw(_("""Selling rate for item {0} is lower than its {1}. Selling rate should be atleast {2}""")
|
frappe.throw(_("""Row #{}: Selling rate for item {} is lower than its {}. Selling rate should be atleast {}""")
|
||||||
.format(item_name, ref_rate_field, rate))
|
.format(idx, item_name, ref_rate_field, rate))
|
||||||
|
|
||||||
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||||
return
|
return
|
||||||
@@ -182,7 +182,7 @@ class SellingController(StockController):
|
|||||||
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
||||||
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
|
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
|
||||||
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
|
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
|
||||||
throw_message(it.item_name, last_purchase_rate_in_sales_uom, "last purchase rate")
|
throw_message(it.idx, frappe.bold(it.item_name), last_purchase_rate_in_sales_uom, "last purchase rate")
|
||||||
|
|
||||||
last_valuation_rate = frappe.db.sql("""
|
last_valuation_rate = frappe.db.sql("""
|
||||||
SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s
|
SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s
|
||||||
@@ -192,7 +192,7 @@ class SellingController(StockController):
|
|||||||
if last_valuation_rate:
|
if last_valuation_rate:
|
||||||
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
|
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
|
||||||
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom):
|
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom):
|
||||||
throw_message(it.name, last_valuation_rate_in_sales_uom, "valuation rate")
|
throw_message(it.idx, frappe.bold(it.item_name), last_valuation_rate_in_sales_uom, "valuation rate")
|
||||||
|
|
||||||
|
|
||||||
def get_item_list(self):
|
def get_item_list(self):
|
||||||
|
|||||||
@@ -69,17 +69,6 @@ status_map = {
|
|||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
["Closed", "eval:self.status=='Closed'"],
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
],
|
],
|
||||||
"Purchase Invoice": [
|
|
||||||
["Draft", None],
|
|
||||||
["Submitted", "eval:self.docstatus==1"],
|
|
||||||
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
|
|
||||||
["Return", "eval:self.is_return==1 and self.docstatus==1"],
|
|
||||||
["Debit Note Issued",
|
|
||||||
"eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
|
|
||||||
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
|
|
||||||
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
|
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
|
||||||
],
|
|
||||||
"Material Request": [
|
"Material Request": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["Stopped", "eval:self.status == 'Stopped'"],
|
["Stopped", "eval:self.status == 'Stopped'"],
|
||||||
|
|||||||
@@ -643,8 +643,7 @@ def get_itemised_tax_breakup_html(doc):
|
|||||||
itemised_tax=itemised_tax,
|
itemised_tax=itemised_tax,
|
||||||
itemised_taxable_amount=itemised_taxable_amount,
|
itemised_taxable_amount=itemised_taxable_amount,
|
||||||
tax_accounts=tax_accounts,
|
tax_accounts=tax_accounts,
|
||||||
conversion_rate=doc.conversion_rate,
|
doc=doc
|
||||||
currency=doc.currency
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class EmailCampaign(Document):
|
|||||||
for entry in campaign.get("campaign_schedules"):
|
for entry in campaign.get("campaign_schedules"):
|
||||||
send_after_days.append(entry.send_after_days)
|
send_after_days.append(entry.send_after_days)
|
||||||
try:
|
try:
|
||||||
end_date = add_days(getdate(self.start_date), max(send_after_days))
|
self.end_date = add_days(getdate(self.start_date), max(send_after_days))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
frappe.throw(_("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name))
|
frappe.throw(_("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name))
|
||||||
|
|
||||||
|
|||||||
@@ -366,7 +366,7 @@
|
|||||||
"icon": "fa fa-user",
|
"icon": "fa fa-user",
|
||||||
"idx": 5,
|
"idx": 5,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"modified": "2019-09-19 12:49:02.536647",
|
"modified": "2020-05-11 20:30:02.536647",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Lead",
|
"name": "Lead",
|
||||||
@@ -423,15 +423,6 @@
|
|||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales User"
|
"role": "Sales User"
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Guest",
|
|
||||||
"share": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "lead_name,lead_owner,status",
|
"search_fields": "lead_name,lead_owner,status",
|
||||||
|
|||||||
@@ -1,96 +1,44 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_rename": 1,
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "field:stage_name",
|
"autoname": "field:stage_name",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-10-01 09:28:16.399518",
|
"creation": "2018-10-01 09:28:16.399518",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"stage_name"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "stage_name",
|
"fieldname": "stage_name",
|
||||||
"fieldtype": "Data",
|
"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": "Stage Name",
|
"label": "Stage Name",
|
||||||
"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": 1
|
"unique": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2020-05-20 14:39:33.300588",
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-10-01 09:29:43.230378",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Sales Stage",
|
"name": "Sales Stage",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales Manager",
|
"role": "Sales Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
23
erpnext/crm/utils.py
Normal file
23
erpnext/crm/utils.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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.db_set("phone", phone)
|
||||||
|
lead.db_set("mobile_no", mobile_no)
|
||||||
@@ -1,790 +1,207 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "EDU-ASP-.YYYY.-.#####",
|
"autoname": "EDU-ASP-.YYYY.-.#####",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2015-11-12 16:34:34.658092",
|
"creation": "2015-11-12 16:34:34.658092",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Setup",
|
"document_type": "Setup",
|
||||||
"editable_grid": 0,
|
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"student_group",
|
||||||
|
"assessment_name",
|
||||||
|
"assessment_group",
|
||||||
|
"grading_scale",
|
||||||
|
"column_break_2",
|
||||||
|
"course",
|
||||||
|
"program",
|
||||||
|
"academic_year",
|
||||||
|
"academic_term",
|
||||||
|
"section_break_5",
|
||||||
|
"schedule_date",
|
||||||
|
"room",
|
||||||
|
"examiner",
|
||||||
|
"examiner_name",
|
||||||
|
"column_break_4",
|
||||||
|
"from_time",
|
||||||
|
"to_time",
|
||||||
|
"supervisor",
|
||||||
|
"supervisor_name",
|
||||||
|
"section_break_20",
|
||||||
|
"maximum_assessment_score",
|
||||||
|
"assessment_criteria",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "student_group",
|
"fieldname": "student_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Student Group",
|
"label": "Student Group",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Student Group",
|
"options": "Student Group",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "assessment_name",
|
"fieldname": "assessment_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
"label": "Assessment Name"
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Assessment Name",
|
|
||||||
"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,
|
|
||||||
"fieldname": "assessment_group",
|
"fieldname": "assessment_group",
|
||||||
"fieldtype": "Link",
|
"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": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Assessment Group",
|
"label": "Assessment Group",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Assessment Group",
|
"options": "Assessment Group",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_from": "course.default_grading_scale",
|
"fetch_from": "course.default_grading_scale",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "grading_scale",
|
"fieldname": "grading_scale",
|
||||||
"fieldtype": "Link",
|
"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": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Grading Scale",
|
"label": "Grading Scale",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Grading Scale",
|
"options": "Grading Scale",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_2",
|
"fieldname": "column_break_2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_from": "student_group.course",
|
"fetch_from": "student_group.course",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "course",
|
"fieldname": "course",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Course",
|
"label": "Course",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Course",
|
"options": "Course",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_from": "student_group.program",
|
"fetch_from": "student_group.program",
|
||||||
"fieldname": "program",
|
"fieldname": "program",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Program",
|
"label": "Program",
|
||||||
"length": 0,
|
"options": "Program"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Program",
|
|
||||||
"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_from": "student_group.academic_year",
|
"fetch_from": "student_group.academic_year",
|
||||||
"fieldname": "academic_year",
|
"fieldname": "academic_year",
|
||||||
"fieldtype": "Link",
|
"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": "Academic Year",
|
"label": "Academic Year",
|
||||||
"length": 0,
|
"options": "Academic Year"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Academic Year",
|
|
||||||
"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_from": "student_group.academic_term",
|
"fetch_from": "student_group.academic_term",
|
||||||
"fieldname": "academic_term",
|
"fieldname": "academic_term",
|
||||||
"fieldtype": "Link",
|
"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": "Academic Term",
|
"label": "Academic Term",
|
||||||
"length": 0,
|
"options": "Academic Term"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Academic Term",
|
|
||||||
"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,
|
|
||||||
"collapsible_depends_on": "",
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "",
|
|
||||||
"fieldname": "section_break_5",
|
"fieldname": "section_break_5",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Schedule"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Schedule",
|
|
||||||
"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,
|
|
||||||
"default": "Today",
|
"default": "Today",
|
||||||
"fieldname": "schedule_date",
|
"fieldname": "schedule_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Schedule Date",
|
"label": "Schedule Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "room",
|
"fieldname": "room",
|
||||||
"fieldtype": "Link",
|
"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": "Room",
|
"label": "Room",
|
||||||
"length": 0,
|
"options": "Room"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Room",
|
|
||||||
"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,
|
|
||||||
"fieldname": "examiner",
|
"fieldname": "examiner",
|
||||||
"fieldtype": "Link",
|
"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": "Examiner",
|
"label": "Examiner",
|
||||||
"length": 0,
|
"options": "Instructor"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Instructor",
|
|
||||||
"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_from": "examiner.instructor_name",
|
"fetch_from": "examiner.instructor_name",
|
||||||
"fieldname": "examiner_name",
|
"fieldname": "examiner_name",
|
||||||
"fieldtype": "Data",
|
"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": "Examiner Name",
|
"label": "Examiner Name",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"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,
|
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "from_time",
|
"fieldname": "from_time",
|
||||||
"fieldtype": "Time",
|
"fieldtype": "Time",
|
||||||
"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": "From Time",
|
"label": "From Time",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "to_time",
|
"fieldname": "to_time",
|
||||||
"fieldtype": "Time",
|
"fieldtype": "Time",
|
||||||
"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": "To Time",
|
"label": "To Time",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "supervisor",
|
"fieldname": "supervisor",
|
||||||
"fieldtype": "Link",
|
"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": "Supervisor",
|
"label": "Supervisor",
|
||||||
"length": 0,
|
"options": "Instructor"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Instructor",
|
|
||||||
"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_from": "supervisor.instructor_name",
|
"fetch_from": "supervisor.instructor_name",
|
||||||
"fieldname": "supervisor_name",
|
"fieldname": "supervisor_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Supervisor Name",
|
"label": "Supervisor Name",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"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,
|
|
||||||
"fieldname": "section_break_20",
|
"fieldname": "section_break_20",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Evaluate"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Evaluate",
|
|
||||||
"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,
|
|
||||||
"fieldname": "maximum_assessment_score",
|
"fieldname": "maximum_assessment_score",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"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": "Maximum Assessment Score",
|
"label": "Maximum Assessment Score",
|
||||||
"length": 0,
|
"reqd": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "assessment_criteria",
|
"fieldname": "assessment_criteria",
|
||||||
"fieldtype": "Table",
|
"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": "Assessment Criteria",
|
"label": "Assessment Criteria",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Assessment Plan Criteria",
|
"options": "Assessment Plan Criteria",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Assessment Plan",
|
"options": "Assessment Plan",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"links": [],
|
||||||
"istable": 0,
|
"modified": "2020-05-09 16:44:04.313175",
|
||||||
"max_attachments": 0,
|
|
||||||
"menu_index": 0,
|
|
||||||
"modified": "2018-08-30 00:48:03.475522",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Assessment Plan",
|
"name": "Assessment Plan",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -794,28 +211,17 @@
|
|||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Academics User",
|
"role": "Academics User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"restrict_to_domain": "Education",
|
"restrict_to_domain": "Education",
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "assessment_name",
|
"title_field": "assessment_name"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2017-04-05 13:33:04.519313",
|
"creation": "2017-04-05 13:33:04.519313",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@@ -42,12 +43,14 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.",
|
"description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.",
|
||||||
"fieldname": "validate_batch",
|
"fieldname": "validate_batch",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Validate Batch for Students in Student Group"
|
"label": "Validate Batch for Students in Student Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.",
|
"description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.",
|
||||||
"fieldname": "validate_course",
|
"fieldname": "validate_course",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@@ -74,13 +77,13 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "web_academy_settings_section",
|
"fieldname": "web_academy_settings_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "LMS Settings"
|
"label": "Learning Management System Settings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.enable_lms",
|
"depends_on": "eval: doc.enable_lms",
|
||||||
"fieldname": "portal_title",
|
"fieldname": "portal_title",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "LMS Title"
|
"label": "Learning Management System Title"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.enable_lms",
|
"depends_on": "eval: doc.enable_lms",
|
||||||
@@ -89,9 +92,10 @@
|
|||||||
"label": "Description"
|
"label": "Description"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "enable_lms",
|
"fieldname": "enable_lms",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Enable LMS"
|
"label": "Enable Learning Management System"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -102,7 +106,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2019-05-13 18:36:13.127563",
|
"links": [],
|
||||||
|
"modified": "2020-05-07 19:50:22.430576",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Education Settings",
|
"name": "Education Settings",
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ frappe.ui.form.on("Fees", {
|
|||||||
args: {
|
args: {
|
||||||
"dt": frm.doc.doctype,
|
"dt": frm.doc.doctype,
|
||||||
"dn": frm.doc.name,
|
"dn": frm.doc.name,
|
||||||
|
"party_type": "Student",
|
||||||
|
"party": frm.doc.student,
|
||||||
"recipient_id": frm.doc.student_email
|
"recipient_id": frm.doc.student_email
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ class Fees(AccountsController):
|
|||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
if self.send_payment_request and self.student_email:
|
if self.send_payment_request and self.student_email:
|
||||||
pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.student_email,
|
pr = make_payment_request(party_type="Student", party=self.student, dt="Fees",
|
||||||
|
dn=self.name, recipient_id=self.student_email,
|
||||||
submit_doc=True, use_dummy_message=True)
|
submit_doc=True, use_dummy_message=True)
|
||||||
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
|
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
|
||||||
|
|
||||||
@@ -96,14 +97,16 @@ class Fees(AccountsController):
|
|||||||
"debit_in_account_currency": self.grand_total,
|
"debit_in_account_currency": self.grand_total,
|
||||||
"against_voucher": self.name,
|
"against_voucher": self.name,
|
||||||
"against_voucher_type": self.doctype
|
"against_voucher_type": self.doctype
|
||||||
})
|
}, item=self)
|
||||||
|
|
||||||
fee_gl_entry = self.get_gl_dict({
|
fee_gl_entry = self.get_gl_dict({
|
||||||
"account": self.income_account,
|
"account": self.income_account,
|
||||||
"against": self.student,
|
"against": self.student,
|
||||||
"credit": self.grand_total,
|
"credit": self.grand_total,
|
||||||
"credit_in_account_currency": self.grand_total,
|
"credit_in_account_currency": self.grand_total,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
|
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2),
|
make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2),
|
||||||
update_outstanding="Yes", merge_entries=False)
|
update_outstanding="Yes", merge_entries=False)
|
||||||
|
|||||||
@@ -144,6 +144,10 @@ def create_sales_order(order, woocommerce_settings, customer_name, sys_lang):
|
|||||||
def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang):
|
def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang):
|
||||||
company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr')
|
company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr')
|
||||||
|
|
||||||
|
default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr)
|
||||||
|
if not frappe.db.exists("Warehouse", default_warehouse):
|
||||||
|
frappe.throw(_("Please set Warehouse in Woocommerce Settings"))
|
||||||
|
|
||||||
for item in order.get("line_items"):
|
for item in order.get("line_items"):
|
||||||
woocomm_item_id = item.get("product_id")
|
woocomm_item_id = item.get("product_id")
|
||||||
found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id})
|
found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id})
|
||||||
@@ -158,7 +162,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l
|
|||||||
"uom": woocommerce_settings.uom or _("Nos", sys_lang),
|
"uom": woocommerce_settings.uom or _("Nos", sys_lang),
|
||||||
"qty": item.get("quantity"),
|
"qty": item.get("quantity"),
|
||||||
"rate": item.get("price"),
|
"rate": item.get("price"),
|
||||||
"warehouse": woocommerce_settings.warehouse or _("Stores - {0}", sys_lang).format(company_abbr)
|
"warehouse": woocommerce_settings.warehouse or default_warehouse
|
||||||
})
|
})
|
||||||
|
|
||||||
add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
|
add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ def new_bank_transaction(transaction):
|
|||||||
result.append(new_transaction.name)
|
result.append(new_transaction.name)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(title=_('Bank transaction creation error'))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,40 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Tally Migration', {
|
frappe.ui.form.on('Tally Migration', {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
|
let reload_status = true;
|
||||||
frappe.realtime.on("tally_migration_progress_update", function (data) {
|
frappe.realtime.on("tally_migration_progress_update", function (data) {
|
||||||
|
if (reload_status) {
|
||||||
|
frappe.model.with_doc(frm.doc.doctype, frm.doc.name, () => {
|
||||||
|
frm.refresh_header();
|
||||||
|
});
|
||||||
|
reload_status = false;
|
||||||
|
}
|
||||||
frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message);
|
frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message);
|
||||||
if (data.count == data.total) {
|
let error_occurred = data.count === -1;
|
||||||
window.setTimeout(title => frm.dashboard.hide_progress(title), 1500, data.title);
|
if (data.count == data.total || error_occurred) {
|
||||||
|
window.setTimeout((title) => {
|
||||||
|
frm.dashboard.hide_progress(title);
|
||||||
|
frm.reload_doc();
|
||||||
|
if (error_occurred) {
|
||||||
|
frappe.msgprint({
|
||||||
|
message: __("An error has occurred during {0}. Check {1} for more details",
|
||||||
|
[
|
||||||
|
repl("<a href='#Form/Tally Migration/%(tally_document)s' class='variant-click'>%(tally_document)s</a>", {
|
||||||
|
tally_document: frm.docname
|
||||||
|
}),
|
||||||
|
"<a href='#List/Error Log' class='variant-click'>Error Log</a>"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
title: __("Tally Migration Error"),
|
||||||
|
indicator: "red"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 2000, data.title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.master_data && !frm.doc.is_master_data_imported) {
|
if (frm.doc.master_data && !frm.doc.is_master_data_imported) {
|
||||||
if (frm.doc.is_master_data_processed) {
|
if (frm.doc.is_master_data_processed) {
|
||||||
if (frm.doc.status != "Importing Master Data") {
|
if (frm.doc.status != "Importing Master Data") {
|
||||||
@@ -34,17 +59,17 @@ frappe.ui.form.on('Tally Migration', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
add_button: function(frm, label, method) {
|
add_button: function (frm, label, method) {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
label,
|
label,
|
||||||
() => frm.call({
|
() => {
|
||||||
doc: frm.doc,
|
frm.call({
|
||||||
method: method,
|
doc: frm.doc,
|
||||||
freeze: true,
|
method: method,
|
||||||
callback: () => {
|
freeze: true
|
||||||
frm.remove_custom_button(label);
|
});
|
||||||
}
|
frm.reload_doc();
|
||||||
})
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"beta": 1,
|
"beta": 1,
|
||||||
"creation": "2019-02-01 14:27:09.485238",
|
"creation": "2019-02-01 14:27:09.485238",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
"tally_debtors_account",
|
"tally_debtors_account",
|
||||||
"company_section",
|
"company_section",
|
||||||
"tally_company",
|
"tally_company",
|
||||||
|
"default_uom",
|
||||||
"column_break_8",
|
"column_break_8",
|
||||||
"erpnext_company",
|
"erpnext_company",
|
||||||
"processed_files_section",
|
"processed_files_section",
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
"label": "Status"
|
"label": "Status"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "Data exported from Tally that consists of the Chart of Accounts, Customers, Suppliers, Addresses, Items and UOMs",
|
||||||
"fieldname": "master_data",
|
"fieldname": "master_data",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -50,6 +53,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Sundry Creditors",
|
"default": "Sundry Creditors",
|
||||||
|
"description": "Creditors Account set in Tally",
|
||||||
"fieldname": "tally_creditors_account",
|
"fieldname": "tally_creditors_account",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Tally Creditors Account",
|
"label": "Tally Creditors Account",
|
||||||
@@ -61,6 +65,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Sundry Debtors",
|
"default": "Sundry Debtors",
|
||||||
|
"description": "Debtors Account set in Tally",
|
||||||
"fieldname": "tally_debtors_account",
|
"fieldname": "tally_debtors_account",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Tally Debtors Account",
|
"label": "Tally Debtors Account",
|
||||||
@@ -72,6 +77,7 @@
|
|||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "Company Name as per Imported Tally Data",
|
||||||
"fieldname": "tally_company",
|
"fieldname": "tally_company",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Tally Company",
|
"label": "Tally Company",
|
||||||
@@ -82,9 +88,11 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "Your Company set in ERPNext",
|
||||||
"fieldname": "erpnext_company",
|
"fieldname": "erpnext_company",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "ERPNext Company"
|
"label": "ERPNext Company",
|
||||||
|
"read_only_depends_on": "eval:doc.is_master_data_processed == 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "processed_files_section",
|
"fieldname": "processed_files_section",
|
||||||
@@ -155,24 +163,28 @@
|
|||||||
"options": "Cost Center"
|
"options": "Cost Center"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "is_master_data_processed",
|
"fieldname": "is_master_data_processed",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Master Data Processed",
|
"label": "Is Master Data Processed",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "is_day_book_data_processed",
|
"fieldname": "is_day_book_data_processed",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Day Book Data Processed",
|
"label": "Is Day Book Data Processed",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "is_day_book_data_imported",
|
"fieldname": "is_day_book_data_imported",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Day Book Data Imported",
|
"label": "Is Day Book Data Imported",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "is_master_data_imported",
|
"fieldname": "is_master_data_imported",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Master Data Imported",
|
"label": "Is Master Data Imported",
|
||||||
@@ -188,13 +200,23 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "Day Book Data exported from Tally that consists of all historic transactions",
|
||||||
"fieldname": "day_book_data",
|
"fieldname": "day_book_data",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Day Book Data"
|
"label": "Day Book Data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Unit",
|
||||||
|
"description": "UOM in case unspecified in imported data",
|
||||||
|
"fieldname": "default_uom",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Default UOM",
|
||||||
|
"options": "UOM"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-04-29 05:46:54.394967",
|
"links": [],
|
||||||
|
"modified": "2020-04-16 13:03:28.894919",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "ERPNext Integrations",
|
"module": "ERPNext Integrations",
|
||||||
"name": "Tally Migration",
|
"name": "Tally Migration",
|
||||||
|
|||||||
@@ -4,20 +4,23 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from decimal import Decimal
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup as bs
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from erpnext import encode_company_abbr
|
||||||
|
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.naming import getseries, revert_series_if_last
|
from frappe.model.naming import getseries, revert_series_if_last
|
||||||
from frappe.utils.data import format_datetime
|
from frappe.utils.data import format_datetime
|
||||||
from bs4 import BeautifulSoup as bs
|
|
||||||
from erpnext import encode_company_abbr
|
|
||||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
|
|
||||||
|
|
||||||
PRIMARY_ACCOUNT = "Primary"
|
PRIMARY_ACCOUNT = "Primary"
|
||||||
VOUCHER_CHUNK_SIZE = 500
|
VOUCHER_CHUNK_SIZE = 500
|
||||||
@@ -39,13 +42,15 @@ class TallyMigration(Document):
|
|||||||
return string
|
return string
|
||||||
|
|
||||||
master_file = frappe.get_doc("File", {"file_url": data_file})
|
master_file = frappe.get_doc("File", {"file_url": data_file})
|
||||||
|
master_file_path = master_file.get_full_path()
|
||||||
|
|
||||||
with zipfile.ZipFile(master_file.get_full_path()) as zf:
|
if zipfile.is_zipfile(master_file_path):
|
||||||
encoded_content = zf.read(zf.namelist()[0])
|
with zipfile.ZipFile(master_file_path) as zf:
|
||||||
try:
|
encoded_content = zf.read(zf.namelist()[0])
|
||||||
content = encoded_content.decode("utf-8-sig")
|
try:
|
||||||
except UnicodeDecodeError:
|
content = encoded_content.decode("utf-8-sig")
|
||||||
content = encoded_content.decode("utf-16")
|
except UnicodeDecodeError:
|
||||||
|
content = encoded_content.decode("utf-16")
|
||||||
|
|
||||||
master = bs(sanitize(emptify(content)), "xml")
|
master = bs(sanitize(emptify(content)), "xml")
|
||||||
collection = master.BODY.IMPORTDATA.REQUESTDATA
|
collection = master.BODY.IMPORTDATA.REQUESTDATA
|
||||||
@@ -58,13 +63,14 @@ class TallyMigration(Document):
|
|||||||
"file_name": key + ".json",
|
"file_name": key + ".json",
|
||||||
"attached_to_doctype": self.doctype,
|
"attached_to_doctype": self.doctype,
|
||||||
"attached_to_name": self.name,
|
"attached_to_name": self.name,
|
||||||
"content": json.dumps(value)
|
"content": json.dumps(value),
|
||||||
|
"is_private": True
|
||||||
}).insert()
|
}).insert()
|
||||||
setattr(self, key, f.file_url)
|
setattr(self, key, f.file_url)
|
||||||
|
|
||||||
def _process_master_data(self):
|
def _process_master_data(self):
|
||||||
def get_company_name(collection):
|
def get_company_name(collection):
|
||||||
return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string
|
return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip()
|
||||||
|
|
||||||
def get_coa_customers_suppliers(collection):
|
def get_coa_customers_suppliers(collection):
|
||||||
root_type_map = {
|
root_type_map = {
|
||||||
@@ -97,17 +103,17 @@ class TallyMigration(Document):
|
|||||||
# If Ledger doesn't have PARENT field then don't create Account
|
# If Ledger doesn't have PARENT field then don't create Account
|
||||||
# For example "Profit & Loss A/c"
|
# For example "Profit & Loss A/c"
|
||||||
if account.PARENT:
|
if account.PARENT:
|
||||||
yield account.PARENT.string, account["NAME"], 0
|
yield account.PARENT.string.strip(), account["NAME"], 0
|
||||||
|
|
||||||
def get_parent(account):
|
def get_parent(account):
|
||||||
if account.PARENT:
|
if account.PARENT:
|
||||||
return account.PARENT.string
|
return account.PARENT.string.strip()
|
||||||
return {
|
return {
|
||||||
("Yes", "No"): "Application of Funds (Assets)",
|
("Yes", "No"): "Application of Funds (Assets)",
|
||||||
("Yes", "Yes"): "Expenses",
|
("Yes", "Yes"): "Expenses",
|
||||||
("No", "Yes"): "Income",
|
("No", "Yes"): "Income",
|
||||||
("No", "No"): "Source of Funds (Liabilities)",
|
("No", "No"): "Source of Funds (Liabilities)",
|
||||||
}[(account.ISDEEMEDPOSITIVE.string, account.ISREVENUE.string)]
|
}[(account.ISDEEMEDPOSITIVE.string.strip(), account.ISREVENUE.string.strip())]
|
||||||
|
|
||||||
def get_children_and_parent_dict(accounts):
|
def get_children_and_parent_dict(accounts):
|
||||||
children, parents = {}, {}
|
children, parents = {}, {}
|
||||||
@@ -145,38 +151,38 @@ class TallyMigration(Document):
|
|||||||
parties, addresses = [], []
|
parties, addresses = [], []
|
||||||
for account in collection.find_all("LEDGER"):
|
for account in collection.find_all("LEDGER"):
|
||||||
party_type = None
|
party_type = None
|
||||||
if account.NAME.string in customers:
|
if account.NAME.string.strip() in customers:
|
||||||
party_type = "Customer"
|
party_type = "Customer"
|
||||||
parties.append({
|
parties.append({
|
||||||
"doctype": party_type,
|
"doctype": party_type,
|
||||||
"customer_name": account.NAME.string,
|
"customer_name": account.NAME.string.strip(),
|
||||||
"tax_id": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None,
|
"tax_id": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
|
||||||
"customer_group": "All Customer Groups",
|
"customer_group": "All Customer Groups",
|
||||||
"territory": "All Territories",
|
"territory": "All Territories",
|
||||||
"customer_type": "Individual",
|
"customer_type": "Individual",
|
||||||
})
|
})
|
||||||
elif account.NAME.string in suppliers:
|
elif account.NAME.string.strip() in suppliers:
|
||||||
party_type = "Supplier"
|
party_type = "Supplier"
|
||||||
parties.append({
|
parties.append({
|
||||||
"doctype": party_type,
|
"doctype": party_type,
|
||||||
"supplier_name": account.NAME.string,
|
"supplier_name": account.NAME.string.strip(),
|
||||||
"pan": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None,
|
"pan": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
|
||||||
"supplier_group": "All Supplier Groups",
|
"supplier_group": "All Supplier Groups",
|
||||||
"supplier_type": "Individual",
|
"supplier_type": "Individual",
|
||||||
})
|
})
|
||||||
if party_type:
|
if party_type:
|
||||||
address = "\n".join([a.string for a in account.find_all("ADDRESS")])
|
address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")])
|
||||||
addresses.append({
|
addresses.append({
|
||||||
"doctype": "Address",
|
"doctype": "Address",
|
||||||
"address_line1": address[:140].strip(),
|
"address_line1": address[:140].strip(),
|
||||||
"address_line2": address[140:].strip(),
|
"address_line2": address[140:].strip(),
|
||||||
"country": account.COUNTRYNAME.string if account.COUNTRYNAME else None,
|
"country": account.COUNTRYNAME.string.strip() if account.COUNTRYNAME else None,
|
||||||
"state": account.LEDSTATENAME.string if account.LEDSTATENAME else None,
|
"state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
|
||||||
"gst_state": account.LEDSTATENAME.string if account.LEDSTATENAME else None,
|
"gst_state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
|
||||||
"pin_code": account.PINCODE.string if account.PINCODE else None,
|
"pin_code": account.PINCODE.string.strip() if account.PINCODE else None,
|
||||||
"mobile": account.LEDGERPHONE.string if account.LEDGERPHONE else None,
|
"mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
|
||||||
"phone": account.LEDGERPHONE.string if account.LEDGERPHONE else None,
|
"phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
|
||||||
"gstin": account.PARTYGSTIN.string if account.PARTYGSTIN else None,
|
"gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None,
|
||||||
"links": [{"link_doctype": party_type, "link_name": account["NAME"]}],
|
"links": [{"link_doctype": party_type, "link_name": account["NAME"]}],
|
||||||
})
|
})
|
||||||
return parties, addresses
|
return parties, addresses
|
||||||
@@ -184,41 +190,50 @@ class TallyMigration(Document):
|
|||||||
def get_stock_items_uoms(collection):
|
def get_stock_items_uoms(collection):
|
||||||
uoms = []
|
uoms = []
|
||||||
for uom in collection.find_all("UNIT"):
|
for uom in collection.find_all("UNIT"):
|
||||||
uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string})
|
uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string.strip()})
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
for item in collection.find_all("STOCKITEM"):
|
for item in collection.find_all("STOCKITEM"):
|
||||||
|
stock_uom = item.BASEUNITS.string.strip() if item.BASEUNITS else self.default_uom
|
||||||
items.append({
|
items.append({
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
"item_code" : item.NAME.string,
|
"item_code" : item.NAME.string.strip(),
|
||||||
"stock_uom": item.BASEUNITS.string,
|
"stock_uom": stock_uom.strip(),
|
||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
"item_group": "All Item Groups",
|
"item_group": "All Item Groups",
|
||||||
"item_defaults": [{"company": self.erpnext_company}]
|
"item_defaults": [{"company": self.erpnext_company}]
|
||||||
})
|
})
|
||||||
|
|
||||||
return items, uoms
|
return items, uoms
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5)
|
||||||
|
collection = self.get_collection(self.master_data)
|
||||||
|
company = get_company_name(collection)
|
||||||
|
self.tally_company = company
|
||||||
|
self.erpnext_company = company
|
||||||
|
|
||||||
self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5)
|
self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5)
|
||||||
collection = self.get_collection(self.master_data)
|
chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection)
|
||||||
|
|
||||||
company = get_company_name(collection)
|
self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5)
|
||||||
self.tally_company = company
|
parties, addresses = get_parties_addresses(collection, customers, suppliers)
|
||||||
self.erpnext_company = company
|
|
||||||
|
|
||||||
self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5)
|
self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5)
|
||||||
chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection)
|
items, uoms = get_stock_items_uoms(collection)
|
||||||
self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5)
|
data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms}
|
||||||
parties, addresses = get_parties_addresses(collection, customers, suppliers)
|
|
||||||
self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5)
|
|
||||||
items, uoms = get_stock_items_uoms(collection)
|
|
||||||
data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms}
|
|
||||||
self.publish("Process Master Data", _("Done"), 5, 5)
|
|
||||||
|
|
||||||
self.dump_processed_data(data)
|
self.publish("Process Master Data", _("Done"), 5, 5)
|
||||||
self.is_master_data_processed = 1
|
self.dump_processed_data(data)
|
||||||
self.status = ""
|
|
||||||
self.save()
|
self.is_master_data_processed = 1
|
||||||
|
|
||||||
|
except:
|
||||||
|
self.publish("Process Master Data", _("Process Failed"), -1, 5)
|
||||||
|
self.log()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def publish(self, title, message, count, total):
|
def publish(self, title, message, count, total):
|
||||||
frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total})
|
frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total})
|
||||||
@@ -256,7 +271,6 @@ class TallyMigration(Document):
|
|||||||
except:
|
except:
|
||||||
self.log(address)
|
self.log(address)
|
||||||
|
|
||||||
|
|
||||||
def create_items_uoms(items_file_url, uoms_file_url):
|
def create_items_uoms(items_file_url, uoms_file_url):
|
||||||
uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url})
|
uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url})
|
||||||
for uom in json.loads(uoms_file.get_content()):
|
for uom in json.loads(uoms_file.get_content()):
|
||||||
@@ -273,25 +287,35 @@ class TallyMigration(Document):
|
|||||||
except:
|
except:
|
||||||
self.log(item)
|
self.log(item)
|
||||||
|
|
||||||
self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4)
|
try:
|
||||||
create_company_and_coa(self.chart_of_accounts)
|
self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4)
|
||||||
self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4)
|
create_company_and_coa(self.chart_of_accounts)
|
||||||
create_parties_and_addresses(self.parties, self.addresses)
|
|
||||||
self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4)
|
self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4)
|
||||||
create_items_uoms(self.items, self.uoms)
|
create_parties_and_addresses(self.parties, self.addresses)
|
||||||
self.publish("Import Master Data", _("Done"), 4, 4)
|
|
||||||
self.status = ""
|
self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4)
|
||||||
self.is_master_data_imported = 1
|
create_items_uoms(self.items, self.uoms)
|
||||||
self.save()
|
|
||||||
|
self.publish("Import Master Data", _("Done"), 4, 4)
|
||||||
|
|
||||||
|
self.is_master_data_imported = 1
|
||||||
|
|
||||||
|
except:
|
||||||
|
self.publish("Import Master Data", _("Process Failed"), -1, 5)
|
||||||
|
self.log()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def _process_day_book_data(self):
|
def _process_day_book_data(self):
|
||||||
def get_vouchers(collection):
|
def get_vouchers(collection):
|
||||||
vouchers = []
|
vouchers = []
|
||||||
for voucher in collection.find_all("VOUCHER"):
|
for voucher in collection.find_all("VOUCHER"):
|
||||||
if voucher.ISCANCELLED.string == "Yes":
|
if voucher.ISCANCELLED.string.strip() == "Yes":
|
||||||
continue
|
continue
|
||||||
inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST")
|
inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST")
|
||||||
if voucher.VOUCHERTYPENAME.string not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries:
|
if voucher.VOUCHERTYPENAME.string.strip() not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries:
|
||||||
function = voucher_to_invoice
|
function = voucher_to_invoice
|
||||||
else:
|
else:
|
||||||
function = voucher_to_journal_entry
|
function = voucher_to_journal_entry
|
||||||
@@ -307,15 +331,15 @@ class TallyMigration(Document):
|
|||||||
accounts = []
|
accounts = []
|
||||||
ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
|
ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
|
||||||
for entry in ledger_entries:
|
for entry in ledger_entries:
|
||||||
account = {"account": encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company), "cost_center": self.default_cost_center}
|
account = {"account": encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company), "cost_center": self.default_cost_center}
|
||||||
if entry.ISPARTYLEDGER.string == "Yes":
|
if entry.ISPARTYLEDGER.string.strip() == "Yes":
|
||||||
party_details = get_party(entry.LEDGERNAME.string)
|
party_details = get_party(entry.LEDGERNAME.string.strip())
|
||||||
if party_details:
|
if party_details:
|
||||||
party_type, party_account = party_details
|
party_type, party_account = party_details
|
||||||
account["party_type"] = party_type
|
account["party_type"] = party_type
|
||||||
account["account"] = party_account
|
account["account"] = party_account
|
||||||
account["party"] = entry.LEDGERNAME.string
|
account["party"] = entry.LEDGERNAME.string.strip()
|
||||||
amount = Decimal(entry.AMOUNT.string)
|
amount = Decimal(entry.AMOUNT.string.strip())
|
||||||
if amount > 0:
|
if amount > 0:
|
||||||
account["credit_in_account_currency"] = str(abs(amount))
|
account["credit_in_account_currency"] = str(abs(amount))
|
||||||
else:
|
else:
|
||||||
@@ -324,21 +348,21 @@ class TallyMigration(Document):
|
|||||||
|
|
||||||
journal_entry = {
|
journal_entry = {
|
||||||
"doctype": "Journal Entry",
|
"doctype": "Journal Entry",
|
||||||
"tally_guid": voucher.GUID.string,
|
"tally_guid": voucher.GUID.string.strip(),
|
||||||
"posting_date": voucher.DATE.string,
|
"posting_date": voucher.DATE.string.strip(),
|
||||||
"company": self.erpnext_company,
|
"company": self.erpnext_company,
|
||||||
"accounts": accounts,
|
"accounts": accounts,
|
||||||
}
|
}
|
||||||
return journal_entry
|
return journal_entry
|
||||||
|
|
||||||
def voucher_to_invoice(voucher):
|
def voucher_to_invoice(voucher):
|
||||||
if voucher.VOUCHERTYPENAME.string in ["Sales", "Credit Note"]:
|
if voucher.VOUCHERTYPENAME.string.strip() in ["Sales", "Credit Note"]:
|
||||||
doctype = "Sales Invoice"
|
doctype = "Sales Invoice"
|
||||||
party_field = "customer"
|
party_field = "customer"
|
||||||
account_field = "debit_to"
|
account_field = "debit_to"
|
||||||
account_name = encode_company_abbr(self.tally_debtors_account, self.erpnext_company)
|
account_name = encode_company_abbr(self.tally_debtors_account, self.erpnext_company)
|
||||||
price_list_field = "selling_price_list"
|
price_list_field = "selling_price_list"
|
||||||
elif voucher.VOUCHERTYPENAME.string in ["Purchase", "Debit Note"]:
|
elif voucher.VOUCHERTYPENAME.string.strip() in ["Purchase", "Debit Note"]:
|
||||||
doctype = "Purchase Invoice"
|
doctype = "Purchase Invoice"
|
||||||
party_field = "supplier"
|
party_field = "supplier"
|
||||||
account_field = "credit_to"
|
account_field = "credit_to"
|
||||||
@@ -351,10 +375,10 @@ class TallyMigration(Document):
|
|||||||
|
|
||||||
invoice = {
|
invoice = {
|
||||||
"doctype": doctype,
|
"doctype": doctype,
|
||||||
party_field: voucher.PARTYNAME.string,
|
party_field: voucher.PARTYNAME.string.strip(),
|
||||||
"tally_guid": voucher.GUID.string,
|
"tally_guid": voucher.GUID.string.strip(),
|
||||||
"posting_date": voucher.DATE.string,
|
"posting_date": voucher.DATE.string.strip(),
|
||||||
"due_date": voucher.DATE.string,
|
"due_date": voucher.DATE.string.strip(),
|
||||||
"items": get_voucher_items(voucher, doctype),
|
"items": get_voucher_items(voucher, doctype),
|
||||||
"taxes": get_voucher_taxes(voucher),
|
"taxes": get_voucher_taxes(voucher),
|
||||||
account_field: account_name,
|
account_field: account_name,
|
||||||
@@ -375,15 +399,15 @@ class TallyMigration(Document):
|
|||||||
for entry in inventory_entries:
|
for entry in inventory_entries:
|
||||||
qty, uom = entry.ACTUALQTY.string.strip().split()
|
qty, uom = entry.ACTUALQTY.string.strip().split()
|
||||||
items.append({
|
items.append({
|
||||||
"item_code": entry.STOCKITEMNAME.string,
|
"item_code": entry.STOCKITEMNAME.string.strip(),
|
||||||
"description": entry.STOCKITEMNAME.string,
|
"description": entry.STOCKITEMNAME.string.strip(),
|
||||||
"qty": qty.strip(),
|
"qty": qty.strip(),
|
||||||
"uom": uom.strip(),
|
"uom": uom.strip(),
|
||||||
"conversion_factor": 1,
|
"conversion_factor": 1,
|
||||||
"price_list_rate": entry.RATE.string.split("/")[0],
|
"price_list_rate": entry.RATE.string.strip().split("/")[0],
|
||||||
"cost_center": self.default_cost_center,
|
"cost_center": self.default_cost_center,
|
||||||
"warehouse": self.default_warehouse,
|
"warehouse": self.default_warehouse,
|
||||||
account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string, self.erpnext_company),
|
account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string.strip(), self.erpnext_company),
|
||||||
})
|
})
|
||||||
return items
|
return items
|
||||||
|
|
||||||
@@ -391,13 +415,13 @@ class TallyMigration(Document):
|
|||||||
ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
|
ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
|
||||||
taxes = []
|
taxes = []
|
||||||
for entry in ledger_entries:
|
for entry in ledger_entries:
|
||||||
if entry.ISPARTYLEDGER.string == "No":
|
if entry.ISPARTYLEDGER.string.strip() == "No":
|
||||||
tax_account = encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company)
|
tax_account = encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company)
|
||||||
taxes.append({
|
taxes.append({
|
||||||
"charge_type": "Actual",
|
"charge_type": "Actual",
|
||||||
"account_head": tax_account,
|
"account_head": tax_account,
|
||||||
"description": tax_account,
|
"description": tax_account,
|
||||||
"tax_amount": entry.AMOUNT.string,
|
"tax_amount": entry.AMOUNT.string.strip(),
|
||||||
"cost_center": self.default_cost_center,
|
"cost_center": self.default_cost_center,
|
||||||
})
|
})
|
||||||
return taxes
|
return taxes
|
||||||
@@ -408,15 +432,24 @@ class TallyMigration(Document):
|
|||||||
elif frappe.db.exists({"doctype": "Customer", "customer_name": party}):
|
elif frappe.db.exists({"doctype": "Customer", "customer_name": party}):
|
||||||
return "Customer", encode_company_abbr(self.tally_debtors_account, self.erpnext_company)
|
return "Customer", encode_company_abbr(self.tally_debtors_account, self.erpnext_company)
|
||||||
|
|
||||||
self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3)
|
try:
|
||||||
collection = self.get_collection(self.day_book_data)
|
self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3)
|
||||||
self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3)
|
collection = self.get_collection(self.day_book_data)
|
||||||
vouchers = get_vouchers(collection)
|
|
||||||
self.publish("Process Day Book Data", _("Done"), 3, 3)
|
self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3)
|
||||||
self.dump_processed_data({"vouchers": vouchers})
|
vouchers = get_vouchers(collection)
|
||||||
self.status = ""
|
|
||||||
self.is_day_book_data_processed = 1
|
self.publish("Process Day Book Data", _("Done"), 3, 3)
|
||||||
self.save()
|
self.dump_processed_data({"vouchers": vouchers})
|
||||||
|
|
||||||
|
self.is_day_book_data_processed = 1
|
||||||
|
|
||||||
|
except:
|
||||||
|
self.publish("Process Day Book Data", _("Process Failed"), -1, 5)
|
||||||
|
self.log()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def _import_day_book_data(self):
|
def _import_day_book_data(self):
|
||||||
def create_fiscal_years(vouchers):
|
def create_fiscal_years(vouchers):
|
||||||
@@ -454,23 +487,31 @@ class TallyMigration(Document):
|
|||||||
"currency": "INR"
|
"currency": "INR"
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable")
|
try:
|
||||||
frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable")
|
frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable")
|
||||||
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account)
|
frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable")
|
||||||
|
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account)
|
||||||
|
|
||||||
vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
|
vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
|
||||||
vouchers = json.loads(vouchers_file.get_content())
|
vouchers = json.loads(vouchers_file.get_content())
|
||||||
|
|
||||||
create_fiscal_years(vouchers)
|
create_fiscal_years(vouchers)
|
||||||
create_price_list()
|
create_price_list()
|
||||||
create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"])
|
create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"])
|
||||||
|
|
||||||
total = len(vouchers)
|
total = len(vouchers)
|
||||||
is_last = False
|
is_last = False
|
||||||
for index in range(0, total, VOUCHER_CHUNK_SIZE):
|
|
||||||
if index + VOUCHER_CHUNK_SIZE >= total:
|
for index in range(0, total, VOUCHER_CHUNK_SIZE):
|
||||||
is_last = True
|
if index + VOUCHER_CHUNK_SIZE >= total:
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last)
|
is_last = True
|
||||||
|
frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last)
|
||||||
|
|
||||||
|
except:
|
||||||
|
self.log()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def _import_vouchers(self, start, total, is_last=False):
|
def _import_vouchers(self, start, total, is_last=False):
|
||||||
frappe.flags.in_migrate = True
|
frappe.flags.in_migrate = True
|
||||||
@@ -494,25 +535,26 @@ class TallyMigration(Document):
|
|||||||
frappe.flags.in_migrate = False
|
frappe.flags.in_migrate = False
|
||||||
|
|
||||||
def process_master_data(self):
|
def process_master_data(self):
|
||||||
self.status = "Processing Master Data"
|
self.set_status("Processing Master Data")
|
||||||
self.save()
|
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
def import_master_data(self):
|
def import_master_data(self):
|
||||||
self.status = "Importing Master Data"
|
self.set_status("Importing Master Data")
|
||||||
self.save()
|
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
def process_day_book_data(self):
|
def process_day_book_data(self):
|
||||||
self.status = "Processing Day Book Data"
|
self.set_status("Processing Day Book Data")
|
||||||
self.save()
|
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
def import_day_book_data(self):
|
def import_day_book_data(self):
|
||||||
self.status = "Importing Day Book Data"
|
self.set_status("Importing Day Book Data")
|
||||||
self.save()
|
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
def log(self, data=None):
|
def log(self, data=None):
|
||||||
message = "\n".join(["Data", json.dumps(data, default=str, indent=4), "Exception", traceback.format_exc()])
|
data = data or self.status
|
||||||
|
message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
|
||||||
return frappe.log_error(title="Tally Migration Error", message=message)
|
return frappe.log_error(title="Tally Migration Error", message=message)
|
||||||
|
|
||||||
|
def set_status(self, status=""):
|
||||||
|
self.status = status
|
||||||
|
self.save()
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ def get_item_dict(table, parent, parenttype):
|
|||||||
def create_stock_entry(doc):
|
def create_stock_entry(doc):
|
||||||
stock_entry = frappe.new_doc("Stock Entry")
|
stock_entry = frappe.new_doc("Stock Entry")
|
||||||
stock_entry = set_stock_items(stock_entry, doc.name, "Clinical Procedure")
|
stock_entry = set_stock_items(stock_entry, doc.name, "Clinical Procedure")
|
||||||
stock_entry.purpose = "Material Issue"
|
stock_entry.stock_entry_type = "Material Issue"
|
||||||
stock_entry.from_warehouse = doc.warehouse
|
stock_entry.from_warehouse = doc.warehouse
|
||||||
stock_entry.company = doc.company
|
stock_entry.company = doc.company
|
||||||
expense_account = get_account(None, "expense_account", "Healthcare Settings", doc.company)
|
expense_account = get_account(None, "expense_account", "Healthcare Settings", doc.company)
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ def create_item_from_template(doc):
|
|||||||
if doc.is_billable:
|
if doc.is_billable:
|
||||||
disabled = 0
|
disabled = 0
|
||||||
|
|
||||||
|
uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
|
||||||
|
|
||||||
#insert item
|
#insert item
|
||||||
item = frappe.get_doc({
|
item = frappe.get_doc({
|
||||||
'doctype': 'Item',
|
'doctype': 'Item',
|
||||||
@@ -92,7 +94,7 @@ def create_item_from_template(doc):
|
|||||||
'show_in_website': 0,
|
'show_in_website': 0,
|
||||||
'is_pro_applicable': 0,
|
'is_pro_applicable': 0,
|
||||||
'disabled': disabled,
|
'disabled': disabled,
|
||||||
'stock_uom': 'Unit'
|
'stock_uom': uom
|
||||||
}).insert(ignore_permissions=True)
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
#insert item price
|
#insert item price
|
||||||
|
|||||||
@@ -69,12 +69,13 @@ class PatientAppointment(Document):
|
|||||||
if fee_validity.ref_invoice:
|
if fee_validity.ref_invoice:
|
||||||
frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True)
|
frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True)
|
||||||
frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till))
|
frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till))
|
||||||
confirm_sms(self)
|
|
||||||
|
|
||||||
if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \
|
if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \
|
||||||
frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1:
|
frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1:
|
||||||
invoice_appointment(self)
|
invoice_appointment(self)
|
||||||
|
|
||||||
|
send_confirmation_msg(self)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def invoice_appointment(appointment_doc):
|
def invoice_appointment(appointment_doc):
|
||||||
if not appointment_doc.name:
|
if not appointment_doc.name:
|
||||||
@@ -303,10 +304,14 @@ def set_pending_appointments():
|
|||||||
"('Scheduled','Open') and appointment_date < %s", today)
|
"('Scheduled','Open') and appointment_date < %s", today)
|
||||||
|
|
||||||
|
|
||||||
def confirm_sms(doc):
|
def send_confirmation_msg(doc):
|
||||||
if frappe.db.get_value("Healthcare Settings", None, "app_con") == '1':
|
if frappe.db.get_single_value("Healthcare Settings", "app_con"):
|
||||||
message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg")
|
message = frappe.db.get_single_value("Healthcare Settings", "app_con_msg")
|
||||||
send_message(doc, message)
|
try:
|
||||||
|
send_message(doc, message)
|
||||||
|
except Exception:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Appointment Confirmation Message Not Sent"))
|
||||||
|
frappe.msgprint(_("Appointment Confirmation Message Not Sent"), indicator="orange")
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_encounter(appointment):
|
def create_encounter(appointment):
|
||||||
@@ -352,7 +357,7 @@ def send_message(doc, message):
|
|||||||
|
|
||||||
# jinja to string convertion happens here
|
# jinja to string convertion happens here
|
||||||
message = frappe.render_template(message, context)
|
message = frappe.render_template(message, context)
|
||||||
number = [patient.mobile]
|
number = [patient_mobile]
|
||||||
send_sms(number, message)
|
send_sms(number, message)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,8 @@ doc_events = {
|
|||||||
},
|
},
|
||||||
"Contact": {
|
"Contact": {
|
||||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
"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": {
|
"Lead": {
|
||||||
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
|
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
|
||||||
@@ -268,7 +269,8 @@ doc_events = {
|
|||||||
|
|
||||||
scheduler_events = {
|
scheduler_events = {
|
||||||
"all": [
|
"all": [
|
||||||
"erpnext.projects.doctype.project.project.project_status_update_reminder"
|
"erpnext.projects.doctype.project.project.project_status_update_reminder",
|
||||||
|
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.set_appointment_reminder"
|
||||||
],
|
],
|
||||||
"hourly": [
|
"hourly": [
|
||||||
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
|
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class Attendance(Document):
|
|||||||
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
|
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
|
||||||
|
|
||||||
# leaves can be marked for future dates
|
# leaves can be marked for future dates
|
||||||
if self.status not in ('On Leave', 'Half Day') and getdate(self.attendance_date) > getdate(nowdate()):
|
if self.status != 'On Leave' and not self.leave_application and getdate(self.attendance_date) > getdate(nowdate()):
|
||||||
frappe.throw(_("Attendance can not be marked for future dates"))
|
frappe.throw(_("Attendance can not be marked for future dates"))
|
||||||
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
|
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
|
||||||
frappe.throw(_("Attendance date can not be less than employee's joining date"))
|
frappe.throw(_("Attendance date can not be less than employee's joining date"))
|
||||||
|
|||||||
@@ -136,9 +136,18 @@ def make_bank_entry(dt, dn):
|
|||||||
def make_return_entry(employee, company, employee_advance_name,
|
def make_return_entry(employee, company, employee_advance_name,
|
||||||
return_amount, advance_account, mode_of_payment=None):
|
return_amount, advance_account, mode_of_payment=None):
|
||||||
return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
|
return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
|
||||||
|
|
||||||
|
mode_of_payment_type = ''
|
||||||
|
if mode_of_payment:
|
||||||
|
mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type')
|
||||||
|
if mode_of_payment_type not in ["Cash", "Bank"]:
|
||||||
|
# if mode of payment is General then it unset the type
|
||||||
|
mode_of_payment_type = None
|
||||||
|
|
||||||
je = frappe.new_doc('Journal Entry')
|
je = frappe.new_doc('Journal Entry')
|
||||||
je.posting_date = nowdate()
|
je.posting_date = nowdate()
|
||||||
je.voucher_type = 'Bank Entry'
|
# if mode of payment is Bank then voucher type is Bank Entry
|
||||||
|
je.voucher_type = '{} Entry'.format(mode_of_payment_type) if mode_of_payment_type else 'Cash Entry'
|
||||||
je.company = company
|
je.company = company
|
||||||
je.remark = 'Return against Employee Advance: ' + employee_advance_name
|
je.remark = 'Return against Employee Advance: ' + employee_advance_name
|
||||||
|
|
||||||
|
|||||||
@@ -76,25 +76,15 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-19 18:06:45.361830",
|
"modified": "2020-05-18 17:17:38.883126",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Other Income",
|
"name": "Employee Other Income",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"create": 1,
|
"amend": 1,
|
||||||
"delete": 1,
|
"cancel": 1,
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"share": 1,
|
|
||||||
"write": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -104,9 +94,12 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "HR Manager",
|
"role": "HR Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 1,
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -116,9 +109,12 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "HR User",
|
"role": "HR User",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 1,
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -128,6 +124,7 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Employee",
|
"role": "Employee",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -115,8 +115,9 @@ class ExpenseClaim(AccountsController):
|
|||||||
"party_type": "Employee",
|
"party_type": "Employee",
|
||||||
"party": self.employee,
|
"party": self.employee,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"against_voucher": self.name
|
"against_voucher": self.name,
|
||||||
})
|
"cost_center": self.cost_center
|
||||||
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
# expense entries
|
# expense entries
|
||||||
@@ -127,8 +128,8 @@ class ExpenseClaim(AccountsController):
|
|||||||
"debit": data.sanctioned_amount,
|
"debit": data.sanctioned_amount,
|
||||||
"debit_in_account_currency": data.sanctioned_amount,
|
"debit_in_account_currency": data.sanctioned_amount,
|
||||||
"against": self.employee,
|
"against": self.employee,
|
||||||
"cost_center": self.cost_center
|
"cost_center": data.cost_center
|
||||||
})
|
}, item=data)
|
||||||
)
|
)
|
||||||
|
|
||||||
for data in self.advances:
|
for data in self.advances:
|
||||||
@@ -156,7 +157,7 @@ class ExpenseClaim(AccountsController):
|
|||||||
"credit": self.grand_total,
|
"credit": self.grand_total,
|
||||||
"credit_in_account_currency": self.grand_total,
|
"credit_in_account_currency": self.grand_total,
|
||||||
"against": self.employee
|
"against": self.employee
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
gl_entry.append(
|
gl_entry.append(
|
||||||
@@ -169,7 +170,7 @@ class ExpenseClaim(AccountsController):
|
|||||||
"debit_in_account_currency": self.grand_total,
|
"debit_in_account_currency": self.grand_total,
|
||||||
"against_voucher": self.name,
|
"against_voucher": self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
return gl_entry
|
return gl_entry
|
||||||
@@ -186,7 +187,7 @@ class ExpenseClaim(AccountsController):
|
|||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"against_voucher": self.name
|
"against_voucher": self.name
|
||||||
})
|
}, item=tax)
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_account_details(self):
|
def validate_account_details(self):
|
||||||
|
|||||||
@@ -10,6 +10,22 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"expense_date",
|
||||||
|
"column_break_2",
|
||||||
|
"expense_type",
|
||||||
|
"default_account",
|
||||||
|
"section_break_4",
|
||||||
|
"description",
|
||||||
|
"section_break_6",
|
||||||
|
"amount",
|
||||||
|
"column_break_8",
|
||||||
|
"sanctioned_amount",
|
||||||
|
"accounting_dimensions_section",
|
||||||
|
"cost_center",
|
||||||
|
"dimension_col_break"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
@@ -348,6 +364,21 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0,
|
"unique": 0,
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "accounting_dimensions_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Accounting Dimensions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dimension_col_break",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@@ -359,12 +390,12 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2019-06-10 08:41:36.122565",
|
"modified": "2020-05-11 18:54:35.601592",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Claim Detail",
|
"name": "Expense Claim Detail",
|
||||||
"owner": "harshada@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
"quick_entry": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
|||||||
@@ -7,14 +7,16 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"account_head",
|
"account_head",
|
||||||
"cost_center",
|
|
||||||
"col_break1",
|
"col_break1",
|
||||||
"rate",
|
"rate",
|
||||||
"description",
|
"description",
|
||||||
"section_break_6",
|
"section_break_6",
|
||||||
"tax_amount",
|
"tax_amount",
|
||||||
"column_break_8",
|
"column_break_8",
|
||||||
"total"
|
"total",
|
||||||
|
"accounting_dimensions_section",
|
||||||
|
"cost_center",
|
||||||
|
"dimension_col_break"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -90,10 +92,20 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_8",
|
"fieldname": "column_break_8",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "accounting_dimensions_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Accounting Dimensions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dimension_col_break",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-06-20 12:01:33.919555",
|
"links": [],
|
||||||
|
"modified": "2020-05-11 19:01:26.611758",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Taxes and Charges",
|
"name": "Expense Taxes and Charges",
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ class LeaveAllocation(Document):
|
|||||||
def validate_leave_allocation_days(self):
|
def validate_leave_allocation_days(self):
|
||||||
company = frappe.db.get_value("Employee", self.employee, "company")
|
company = frappe.db.get_value("Employee", self.employee, "company")
|
||||||
leave_period = get_leave_period(self.from_date, self.to_date, company)
|
leave_period = get_leave_period(self.from_date, self.to_date, company)
|
||||||
max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
|
max_leaves_allowed = flt(frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed"))
|
||||||
if max_leaves_allowed > 0:
|
if max_leaves_allowed > 0:
|
||||||
leave_allocated = 0
|
leave_allocated = 0
|
||||||
if leave_period:
|
if leave_period:
|
||||||
leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type,
|
leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type,
|
||||||
leave_period[0].from_date, leave_period[0].to_date)
|
leave_period[0].from_date, leave_period[0].to_date)
|
||||||
leave_allocated += self.new_leaves_allocated
|
leave_allocated += flt(self.new_leaves_allocated)
|
||||||
if leave_allocated > max_leaves_allowed:
|
if leave_allocated > max_leaves_allowed:
|
||||||
frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")
|
frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")
|
||||||
.format(self.leave_type, self.employee))
|
.format(self.leave_type, self.employee))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.create_leave_ledger_entry()
|
self.create_leave_ledger_entry()
|
||||||
|
|||||||
@@ -541,7 +541,7 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry):
|
|||||||
|
|
||||||
return _get_remaining_leaves(total_leaves, allocation.to_date)
|
return _get_remaining_leaves(total_leaves, allocation.to_date)
|
||||||
|
|
||||||
def get_leaves_for_period(employee, leave_type, from_date, to_date):
|
def get_leaves_for_period(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False):
|
||||||
leave_entries = get_leave_entries(employee, leave_type, from_date, to_date)
|
leave_entries = get_leave_entries(employee, leave_type, from_date, to_date)
|
||||||
leave_days = 0
|
leave_days = 0
|
||||||
|
|
||||||
@@ -551,8 +551,8 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date):
|
|||||||
if inclusive_period and leave_entry.transaction_type == 'Leave Encashment':
|
if inclusive_period and leave_entry.transaction_type == 'Leave Encashment':
|
||||||
leave_days += leave_entry.leaves
|
leave_days += leave_entry.leaves
|
||||||
|
|
||||||
elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
|
elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' and leave_entry.is_expired \
|
||||||
and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date):
|
and (do_not_skip_expired_leaves or not skip_expiry_leaves(leave_entry, to_date)):
|
||||||
leave_days += leave_entry.leaves
|
leave_days += leave_entry.leaves
|
||||||
|
|
||||||
elif leave_entry.transaction_type == 'Leave Application':
|
elif leave_entry.transaction_type == 'Leave Application':
|
||||||
|
|||||||
@@ -88,32 +88,40 @@ def get_previous_expiry_ledger_entry(ledger):
|
|||||||
}, fieldname=['name'])
|
}, fieldname=['name'])
|
||||||
|
|
||||||
def process_expired_allocation():
|
def process_expired_allocation():
|
||||||
''' Check if a carry forwarded allocation has expired and create a expiry ledger entry '''
|
''' Check if a carry forwarded allocation has expired and create a expiry ledger entry
|
||||||
|
Case 1: carry forwarded expiry period is set for the leave type,
|
||||||
|
create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves
|
||||||
|
Case 2: leave type has no specific expiry period for carry forwarded leaves
|
||||||
|
and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves.
|
||||||
|
'''
|
||||||
|
|
||||||
# fetch leave type records that has carry forwarded leaves expiry
|
# fetch leave type records that has carry forwarded leaves expiry
|
||||||
leave_type_records = frappe.db.get_values("Leave Type", filters={
|
leave_type_records = frappe.db.get_values("Leave Type", filters={
|
||||||
'expire_carry_forwarded_leaves_after_days': (">", 0)
|
'expire_carry_forwarded_leaves_after_days': (">", 0)
|
||||||
}, fieldname=['name'])
|
}, fieldname=['name'])
|
||||||
|
|
||||||
leave_type = [record[0] for record in leave_type_records]
|
leave_type = [record[0] for record in leave_type_records] or ['']
|
||||||
|
|
||||||
expired_allocation = frappe.db.sql_list("""SELECT name
|
# fetch non expired leave ledger entry of transaction_type allocation
|
||||||
FROM `tabLeave Ledger Entry`
|
expire_allocation = frappe.db.sql("""
|
||||||
WHERE
|
SELECT
|
||||||
`transaction_type`='Leave Allocation'
|
leaves, to_date, employee, leave_type,
|
||||||
AND `is_expired`=1""")
|
is_carry_forward, transaction_name as name, transaction_type
|
||||||
|
FROM `tabLeave Ledger Entry` l
|
||||||
expire_allocation = frappe.get_all("Leave Ledger Entry",
|
WHERE (NOT EXISTS
|
||||||
fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'],
|
(SELECT name
|
||||||
filters={
|
FROM `tabLeave Ledger Entry`
|
||||||
'to_date': ("<", today()),
|
WHERE
|
||||||
'transaction_type': 'Leave Allocation',
|
transaction_name = l.transaction_name
|
||||||
'transaction_name': ('not in', expired_allocation)
|
AND transaction_type = 'Leave Allocation'
|
||||||
},
|
AND name<>l.name
|
||||||
or_filters={
|
AND docstatus = 1
|
||||||
'is_carry_forward': 0,
|
AND (
|
||||||
'leave_type': ('in', leave_type)
|
is_carry_forward=l.is_carry_forward
|
||||||
})
|
OR (is_carry_forward = 0 AND leave_type not in %s)
|
||||||
|
)))
|
||||||
|
AND transaction_type = 'Leave Allocation'
|
||||||
|
AND to_date < %s""", (leave_type, today()), as_dict=1)
|
||||||
|
|
||||||
if expire_allocation:
|
if expire_allocation:
|
||||||
create_expiry_ledger_entry(expire_allocation)
|
create_expiry_ledger_entry(expire_allocation)
|
||||||
@@ -133,6 +141,7 @@ def get_remaining_leaves(allocation):
|
|||||||
'employee': allocation.employee,
|
'employee': allocation.employee,
|
||||||
'leave_type': allocation.leave_type,
|
'leave_type': allocation.leave_type,
|
||||||
'to_date': ('<=', allocation.to_date),
|
'to_date': ('<=', allocation.to_date),
|
||||||
|
'docstatus': 1
|
||||||
}, fieldname=['SUM(leaves)'])
|
}, fieldname=['SUM(leaves)'])
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -159,7 +168,8 @@ def expire_allocation(allocation, expiry_date=None):
|
|||||||
def expire_carried_forward_allocation(allocation):
|
def expire_carried_forward_allocation(allocation):
|
||||||
''' Expires remaining leaves in the on carried forward allocation '''
|
''' Expires remaining leaves in the on carried forward allocation '''
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
|
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
|
||||||
leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date)
|
leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type,
|
||||||
|
allocation.from_date, allocation.to_date, do_not_skip_expired_leaves=True)
|
||||||
leaves = flt(allocation.leaves) + flt(leaves_taken)
|
leaves = flt(allocation.leaves) + flt(leaves_taken)
|
||||||
|
|
||||||
# allow expired leaves entry to be created
|
# allow expired leaves entry to be created
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ const submit_salary_slip = function (frm) {
|
|||||||
|
|
||||||
let make_bank_entry = function (frm) {
|
let make_bank_entry = function (frm) {
|
||||||
var doc = frm.doc;
|
var doc = frm.doc;
|
||||||
if (doc.company && doc.start_date && doc.end_date && doc.payment_account) {
|
if (doc.payment_account) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
doc: cur_frm.doc,
|
doc: cur_frm.doc,
|
||||||
method: "make_payment_entry",
|
method: "make_payment_entry",
|
||||||
@@ -262,7 +262,8 @@ let make_bank_entry = function (frm) {
|
|||||||
freeze_message: __("Creating Payment Entries......")
|
freeze_message: __("Creating Payment Entries......")
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
frappe.msgprint(__("Company, Payment Account, From Date and To Date is mandatory"));
|
frappe.msgprint(__("Payment Account is mandatory"));
|
||||||
|
frm.scroll_to_field('payment_account');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ def get_existing_assignments(employees, salary_structure, from_date):
|
|||||||
return salary_structures_assignments
|
return salary_structures_assignments
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
|
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0, ignore_permissions=False):
|
||||||
def postprocess(source, target):
|
def postprocess(source, target):
|
||||||
if employee:
|
if employee:
|
||||||
employee_details = frappe.db.get_value("Employee", employee,
|
employee_details = frappe.db.get_value("Employee", employee,
|
||||||
@@ -169,7 +169,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
|
|||||||
"name": "salary_structure"
|
"name": "salary_structure"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, target_doc, postprocess, ignore_child_tables=True)
|
}, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions)
|
||||||
|
|
||||||
if cint(as_print):
|
if cint(as_print):
|
||||||
doc.name = 'Preview for {0}'.format(employee)
|
doc.name = 'Preview for {0}'.format(employee)
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ from frappe.utils import flt
|
|||||||
from erpnext.hr.doctype.leave_application.leave_application \
|
from erpnext.hr.doctype.leave_application.leave_application \
|
||||||
import get_leave_balance_on, get_leaves_for_period
|
import get_leave_balance_on, get_leaves_for_period
|
||||||
|
|
||||||
from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary \
|
|
||||||
import get_department_leave_approver_map
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
|
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
|
||||||
|
|
||||||
@@ -28,6 +25,8 @@ def get_columns(leave_types):
|
|||||||
|
|
||||||
for leave_type in leave_types:
|
for leave_type in leave_types:
|
||||||
columns.append(_(leave_type) + " " + _("Opening") + ":Float:160")
|
columns.append(_(leave_type) + " " + _("Opening") + ":Float:160")
|
||||||
|
columns.append(_(leave_type) + " " + _("Allocated") + ":Float:160")
|
||||||
|
columns.append(_(leave_type) + " " + _("Expired") + ":Float:160")
|
||||||
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
|
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
|
||||||
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
|
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
|
||||||
|
|
||||||
@@ -68,18 +67,97 @@ def get_data(filters, leave_types):
|
|||||||
row = [employee.name, employee.employee_name, employee.department]
|
row = [employee.name, employee.employee_name, employee.department]
|
||||||
|
|
||||||
for leave_type in leave_types:
|
for leave_type in leave_types:
|
||||||
# leaves taken
|
|
||||||
leaves_taken = get_leaves_for_period(employee.name, leave_type,
|
|
||||||
filters.from_date, filters.to_date) * -1
|
|
||||||
|
|
||||||
# opening balance
|
|
||||||
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
|
||||||
|
|
||||||
# closing balance
|
row += calculate_leaves_details(filters, leave_type, employee)
|
||||||
closing = max(opening - leaves_taken, 0)
|
|
||||||
|
|
||||||
row += [opening, leaves_taken, closing]
|
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def calculate_leaves_details(filters, leave_type, employee):
|
||||||
|
ledger_entries = get_leave_ledger_entries(filters.from_date, filters.to_date, employee.name, leave_type)
|
||||||
|
|
||||||
|
#Leaves Deducted consist of both expired and leaves taken
|
||||||
|
leaves_deducted = get_leaves_for_period(employee.name, leave_type,
|
||||||
|
filters.from_date, filters.to_date) * -1
|
||||||
|
|
||||||
|
# removing expired leaves
|
||||||
|
leaves_taken = leaves_deducted - remove_expired_leave(ledger_entries)
|
||||||
|
|
||||||
|
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
||||||
|
|
||||||
|
new_allocation , expired_allocation = get_allocated_and_expired_leaves(ledger_entries, filters.from_date, filters.to_date)
|
||||||
|
|
||||||
|
#removing leaves taken from expired_allocation
|
||||||
|
expired_leaves = max(expired_allocation - leaves_taken, 0)
|
||||||
|
|
||||||
|
#Formula for calculating closing balance
|
||||||
|
closing = max(opening + new_allocation - (leaves_taken + expired_leaves), 0)
|
||||||
|
|
||||||
|
return [opening, new_allocation, expired_leaves, leaves_taken, closing]
|
||||||
|
|
||||||
|
|
||||||
|
def remove_expired_leave(records):
|
||||||
|
expired_within_period = 0
|
||||||
|
for record in records:
|
||||||
|
if record.is_expired:
|
||||||
|
expired_within_period += record.leaves
|
||||||
|
return expired_within_period * -1
|
||||||
|
|
||||||
|
|
||||||
|
def get_allocated_and_expired_leaves(records, from_date, to_date):
|
||||||
|
|
||||||
|
from frappe.utils import getdate
|
||||||
|
|
||||||
|
new_allocation = 0
|
||||||
|
expired_leaves = 0
|
||||||
|
|
||||||
|
for record in records:
|
||||||
|
if record.to_date <= getdate(to_date) and record.leaves>0:
|
||||||
|
expired_leaves += record.leaves
|
||||||
|
|
||||||
|
if record.from_date >= getdate(from_date) and record.leaves>0:
|
||||||
|
new_allocation += record.leaves
|
||||||
|
|
||||||
|
return new_allocation, expired_leaves
|
||||||
|
|
||||||
|
def get_leave_ledger_entries(from_date, to_date, employee, leave_type):
|
||||||
|
records= frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type
|
||||||
|
is_carry_forward, is_expired
|
||||||
|
FROM `tabLeave Ledger Entry`
|
||||||
|
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
||||||
|
AND docstatus=1
|
||||||
|
AND (from_date between %(from_date)s AND %(to_date)s
|
||||||
|
OR to_date between %(from_date)s AND %(to_date)s
|
||||||
|
OR (from_date < %(from_date)s AND to_date > %(to_date)s))
|
||||||
|
""", {
|
||||||
|
"from_date": from_date,
|
||||||
|
"to_date": to_date,
|
||||||
|
"employee": employee,
|
||||||
|
"leave_type": leave_type
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
def get_department_leave_approver_map(department=None):
|
||||||
|
conditions=''
|
||||||
|
if department:
|
||||||
|
conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department}
|
||||||
|
|
||||||
|
# get current department and all its child
|
||||||
|
department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec
|
||||||
|
|
||||||
|
# retrieve approvers list from current department and from its subsequent child departments
|
||||||
|
approver_list = frappe.get_all('Department Approver', filters={
|
||||||
|
'parentfield': 'leave_approvers',
|
||||||
|
'parent': ('in', department_list)
|
||||||
|
}, fields=['parent', 'approver'], as_list=1)
|
||||||
|
|
||||||
|
approvers = {}
|
||||||
|
|
||||||
|
for k, v in approver_list:
|
||||||
|
approvers.setdefault(k, []).append(v)
|
||||||
|
|
||||||
|
return approvers
|
||||||
@@ -6,6 +6,7 @@ import frappe
|
|||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on
|
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on
|
||||||
|
from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calculate_leaves_details , get_department_leave_approver_map
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if filters.to_date <= filters.from_date:
|
if filters.to_date <= filters.from_date:
|
||||||
@@ -38,17 +39,27 @@ def get_columns():
|
|||||||
'label': _('Opening Balance'),
|
'label': _('Opening Balance'),
|
||||||
'fieldtype': 'float',
|
'fieldtype': 'float',
|
||||||
'fieldname': 'opening_balance',
|
'fieldname': 'opening_balance',
|
||||||
'width': 160,
|
'width': 120,
|
||||||
|
}, {
|
||||||
|
'label': _('New Allocation'),
|
||||||
|
'fieldtype': 'Float',
|
||||||
|
'fieldname': 'new_allocation',
|
||||||
|
'width': 120,
|
||||||
|
}, {
|
||||||
|
'label': _('Expired Leaves'),
|
||||||
|
'fieldtype': 'Float',
|
||||||
|
'fieldname': 'expired_leaves',
|
||||||
|
'width': 120,
|
||||||
}, {
|
}, {
|
||||||
'label': _('Leaves Taken'),
|
'label': _('Leaves Taken'),
|
||||||
'fieldtype': 'float',
|
'fieldtype': 'float',
|
||||||
'fieldname': 'leaves_taken',
|
'fieldname': 'leaves_taken',
|
||||||
'width': 160,
|
'width': 120,
|
||||||
}, {
|
}, {
|
||||||
'label': _('Closing Balance'),
|
'label': _('Closing Balance'),
|
||||||
'fieldtype': 'float',
|
'fieldtype': 'float',
|
||||||
'fieldname': 'closing_balance',
|
'fieldname': 'closing_balance',
|
||||||
'width': 160,
|
'width': 120,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
@@ -82,16 +93,13 @@ def get_data(filters):
|
|||||||
'employee_name': employee.employee_name
|
'employee_name': employee.employee_name
|
||||||
})
|
})
|
||||||
|
|
||||||
leaves_taken = get_leaves_for_period(employee.name, leave_type,
|
leave_details = calculate_leaves_details(filters, leave_type, employee)
|
||||||
filters.from_date, filters.to_date) * -1
|
row.opening_balance = flt(leave_details[0])
|
||||||
|
row.new_allocation = flt(leave_details[1])
|
||||||
|
row.expired_leaves = flt(leave_details[2])
|
||||||
|
row.leaves_taken = flt(leave_details[3])
|
||||||
|
row.closing_balance = flt(leave_details[4])
|
||||||
|
|
||||||
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
|
||||||
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date)
|
|
||||||
|
|
||||||
row.opening_balance = opening
|
|
||||||
row.leaves_taken = leaves_taken
|
|
||||||
row.closing_balance = closing
|
|
||||||
row.indent = 1
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@@ -108,23 +116,3 @@ def get_conditions(filters):
|
|||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_department_leave_approver_map(department=None):
|
|
||||||
conditions=''
|
|
||||||
if department:
|
|
||||||
conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department}
|
|
||||||
|
|
||||||
# get current department and all its child
|
|
||||||
department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec
|
|
||||||
|
|
||||||
# retrieve approvers list from current department and from its subsequent child departments
|
|
||||||
approver_list = frappe.get_all('Department Approver', filters={
|
|
||||||
'parentfield': 'leave_approvers',
|
|
||||||
'parent': ('in', department_list)
|
|
||||||
}, fields=['parent', 'approver'], as_list=1)
|
|
||||||
|
|
||||||
approvers = {}
|
|
||||||
|
|
||||||
for k, v in approver_list:
|
|
||||||
approvers.setdefault(k, []).append(v)
|
|
||||||
|
|
||||||
return approvers
|
|
||||||
|
|||||||
@@ -175,6 +175,12 @@ frappe.ui.form.on("BOM", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rm_cost_as_per: function(frm) {
|
||||||
|
if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) {
|
||||||
|
frm.set_value("plc_conversion_rate", 1.0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
routing: function(frm) {
|
routing: function(frm) {
|
||||||
if (frm.doc.routing) {
|
if (frm.doc.routing) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
@@ -205,7 +211,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
|
|||||||
item_code: function(doc, cdt, cdn){
|
item_code: function(doc, cdt, cdn){
|
||||||
var scrap_items = false;
|
var scrap_items = false;
|
||||||
var child = locals[cdt][cdn];
|
var child = locals[cdt][cdn];
|
||||||
if(child.doctype == 'BOM Scrap Item') {
|
if (child.doctype == 'BOM Scrap Item') {
|
||||||
scrap_items = true;
|
scrap_items = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,8 +221,19 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
get_bom_material_detail(doc, cdt, cdn, scrap_items);
|
get_bom_material_detail(doc, cdt, cdn, scrap_items);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buying_price_list: function(doc) {
|
||||||
|
this.apply_price_list();
|
||||||
|
},
|
||||||
|
|
||||||
|
plc_conversion_rate: function(doc) {
|
||||||
|
if (!this.in_apply_price_list) {
|
||||||
|
this.apply_price_list(null, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
conversion_factor: function(doc, cdt, cdn) {
|
conversion_factor: function(doc, cdt, cdn) {
|
||||||
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
|
if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
|
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
|
||||||
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
|
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user