Merge branch 'develop' into maint_sch_link_fix
This commit is contained in:
commit
d39ffeeef0
42
.github/labeler.yml
vendored
42
.github/labeler.yml
vendored
@ -1,53 +1,53 @@
|
||||
accounts:
|
||||
- 'erpnext/accounts/*'
|
||||
- 'erpnext/controllers/accounts_controller.py'
|
||||
- 'erpnext/controllers/taxes_and_totals.py'
|
||||
- erpnext/accounts/*
|
||||
- erpnext/controllers/accounts_controller.py
|
||||
- erpnext/controllers/taxes_and_totals.py
|
||||
|
||||
stock:
|
||||
- 'erpnext/stock/*'
|
||||
- 'erpnext/controllers/stock_controller.py'
|
||||
- 'erpnext/controllers/item_variant.py'
|
||||
- erpnext/stock/*
|
||||
- erpnext/controllers/stock_controller.py
|
||||
- erpnext/controllers/item_variant.py
|
||||
|
||||
assets:
|
||||
- 'erpnext/assets/*'
|
||||
- erpnext/assets/*
|
||||
|
||||
regional:
|
||||
- 'erpnext/regional/*'
|
||||
- erpnext/regional/*
|
||||
|
||||
selling:
|
||||
- 'erpnext/selling/*'
|
||||
- 'erpnext/controllers/selling_controller.py'
|
||||
- erpnext/selling/*
|
||||
- erpnext/controllers/selling_controller.py
|
||||
|
||||
buying:
|
||||
- 'erpnext/buying/*'
|
||||
- 'erpnext/controllers/buying_controller.py'
|
||||
- erpnext/buying/*
|
||||
- erpnext/controllers/buying_controller.py
|
||||
|
||||
support:
|
||||
- 'erpnext/support/*'
|
||||
- erpnext/support/*
|
||||
|
||||
POS:
|
||||
- 'pos*'
|
||||
- pos*
|
||||
|
||||
ecommerce:
|
||||
- 'erpnext/e_commerce/*'
|
||||
- erpnext/e_commerce/*
|
||||
|
||||
maintenance:
|
||||
- 'erpnext/maintenance/*'
|
||||
- erpnext/maintenance/*
|
||||
|
||||
manufacturing:
|
||||
- 'erpnext/manufacturing/*'
|
||||
- erpnext/manufacturing/*
|
||||
|
||||
crm:
|
||||
- 'erpnext/crm/*'
|
||||
- erpnext/crm/*
|
||||
|
||||
HR:
|
||||
- 'erpnext/hr/*'
|
||||
- erpnext/hr/*
|
||||
|
||||
payroll:
|
||||
- 'erpnext/payroll*'
|
||||
- erpnext/payroll*
|
||||
|
||||
projects:
|
||||
- 'erpnext/projects/*'
|
||||
- erpnext/projects/*
|
||||
|
||||
# Any python files modifed but no test files modified
|
||||
needs-tests:
|
||||
|
@ -254,11 +254,13 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
enable_check = "enable_deferred_revenue" \
|
||||
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
|
||||
|
||||
accounts_frozen_upto = frappe.get_cached_value('Accounts Settings', 'None', 'acc_frozen_upto')
|
||||
|
||||
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
|
||||
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
|
||||
if not (start_date and end_date): return
|
||||
|
||||
account_currency = get_account_currency(item.expense_account)
|
||||
account_currency = get_account_currency(item.expense_account or item.income_account)
|
||||
if doc.doctype == "Sales Invoice":
|
||||
against, project = doc.customer, doc.project
|
||||
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
||||
@ -279,6 +281,10 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
if not amount:
|
||||
return
|
||||
|
||||
# check if books nor frozen till endate:
|
||||
if getdate(end_date) >= getdate(accounts_frozen_upto):
|
||||
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
|
||||
|
||||
if via_journal_entry:
|
||||
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
|
||||
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
|
||||
@ -406,8 +412,6 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
||||
'account': credit_account,
|
||||
'credit': base_amount,
|
||||
'credit_in_account_currency': amount,
|
||||
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
|
||||
'party': against,
|
||||
'account_currency': account_currency,
|
||||
'reference_name': doc.name,
|
||||
'reference_type': doc.doctype,
|
||||
@ -420,8 +424,6 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
||||
'account': debit_account,
|
||||
'debit': base_amount,
|
||||
'debit_in_account_currency': amount,
|
||||
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
|
||||
'party': against,
|
||||
'account_currency': account_currency,
|
||||
'reference_name': doc.name,
|
||||
'reference_type': doc.doctype,
|
||||
|
@ -407,13 +407,14 @@ class JournalEntry(AccountsController):
|
||||
debit_or_credit = 'Debit' if d.debit else 'Credit'
|
||||
party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
|
||||
debit_or_credit)
|
||||
against_voucher = ['', against_voucher[1]]
|
||||
else:
|
||||
if d.reference_type == "Sales Invoice":
|
||||
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
|
||||
else:
|
||||
party_account = against_voucher[1]
|
||||
|
||||
if (against_voucher[0] != d.party or party_account != d.account):
|
||||
if (against_voucher[0] != cstr(d.party) or party_account != d.account):
|
||||
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
|
||||
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
|
||||
d.reference_type, d.reference_name))
|
||||
@ -478,13 +479,22 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def set_against_account(self):
|
||||
accounts_debited, accounts_credited = [], []
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
|
||||
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
|
||||
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
|
||||
for d in self.get('accounts'):
|
||||
if d.reference_type == 'Sales Invoice':
|
||||
field = 'customer'
|
||||
else:
|
||||
field = 'supplier'
|
||||
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
||||
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
||||
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
|
||||
else:
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
|
||||
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
|
||||
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
||||
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
||||
|
||||
def validate_debit_credit_amount(self):
|
||||
for d in self.get('accounts'):
|
||||
|
@ -1781,47 +1781,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
||||
|
||||
def test_deferred_revenue_post_account_freeze_upto_by_admin(self):
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
|
||||
|
||||
deferred_account = create_account(account_name="Deferred Revenue",
|
||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||
|
||||
item = create_item("_Test Item for Deferred Accounting")
|
||||
item.enable_deferred_revenue = 1
|
||||
item.deferred_revenue_account = deferred_account
|
||||
item.no_of_months = 12
|
||||
item.save()
|
||||
|
||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_save=True)
|
||||
si.items[0].enable_deferred_revenue = 1
|
||||
si.items[0].service_start_date = "2019-01-10"
|
||||
si.items[0].service_end_date = "2019-03-15"
|
||||
si.items[0].deferred_revenue_account = deferred_account
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
|
||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', 'System Manager')
|
||||
|
||||
pda1 = frappe.get_doc(dict(
|
||||
doctype='Process Deferred Accounting',
|
||||
posting_date=nowdate(),
|
||||
start_date="2019-01-01",
|
||||
end_date="2019-03-31",
|
||||
type="Income",
|
||||
company="_Test Company"
|
||||
))
|
||||
|
||||
pda1.insert()
|
||||
self.assertRaises(frappe.ValidationError, pda1.submit)
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
|
||||
|
||||
def test_fixed_deferred_revenue(self):
|
||||
deferred_account = create_account(account_name="Deferred Revenue",
|
||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||
@ -2482,6 +2441,74 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance)
|
||||
|
||||
def test_multi_currency_deferred_revenue_via_journal_entry(self):
|
||||
deferred_account = create_account(account_name="Deferred Revenue",
|
||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||
|
||||
acc_settings = frappe.get_single('Accounts Settings')
|
||||
acc_settings.book_deferred_entries_via_journal_entry = 1
|
||||
acc_settings.submit_journal_entries = 1
|
||||
acc_settings.save()
|
||||
|
||||
item = create_item("_Test Item for Deferred Accounting")
|
||||
item.enable_deferred_expense = 1
|
||||
item.deferred_revenue_account = deferred_account
|
||||
item.save()
|
||||
|
||||
si = create_sales_invoice(customer='_Test Customer USD', currency='USD',
|
||||
item=item.name, qty=1, rate=100, conversion_rate=60, do_not_save=True)
|
||||
|
||||
si.set_posting_time = 1
|
||||
si.posting_date = '2019-01-01'
|
||||
si.debit_to = '_Test Receivable USD - _TC'
|
||||
si.items[0].enable_deferred_revenue = 1
|
||||
si.items[0].service_start_date = "2019-01-01"
|
||||
si.items[0].service_end_date = "2019-03-30"
|
||||
si.items[0].deferred_expense_account = deferred_account
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
|
||||
|
||||
pda1 = frappe.get_doc(dict(
|
||||
doctype='Process Deferred Accounting',
|
||||
posting_date=nowdate(),
|
||||
start_date="2019-01-01",
|
||||
end_date="2019-03-31",
|
||||
type="Income",
|
||||
company="_Test Company"
|
||||
))
|
||||
|
||||
pda1.insert()
|
||||
pda1.submit()
|
||||
|
||||
expected_gle = [
|
||||
["Sales - _TC", 0.0, 2089.89, "2019-01-28"],
|
||||
[deferred_account, 2089.89, 0.0, "2019-01-28"],
|
||||
["Sales - _TC", 0.0, 1887.64, "2019-02-28"],
|
||||
[deferred_account, 1887.64, 0.0, "2019-02-28"],
|
||||
["Sales - _TC", 0.0, 2022.47, "2019-03-15"],
|
||||
[deferred_account, 2022.47, 0.0, "2019-03-15"]
|
||||
]
|
||||
|
||||
gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
|
||||
from `tabGL Entry`
|
||||
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
|
||||
order by posting_date asc, account asc""", (si.items[0].name, si.posting_date), as_dict=1)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_gle[i][0], gle.account)
|
||||
self.assertEqual(expected_gle[i][1], gle.credit)
|
||||
self.assertEqual(expected_gle[i][2], gle.debit)
|
||||
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
|
||||
|
||||
acc_settings = frappe.get_single('Accounts Settings')
|
||||
acc_settings.book_deferred_entries_via_journal_entry = 0
|
||||
acc_settings.submit_journal_entriessubmit_journal_entries = 0
|
||||
acc_settings.save()
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = 'INV-2020-.#####'
|
||||
|
@ -28,14 +28,14 @@
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "single_threshold",
|
||||
"fieldtype": "Currency",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Single Transaction Threshold"
|
||||
},
|
||||
{
|
||||
"columns": 3,
|
||||
"fieldname": "cumulative_threshold",
|
||||
"fieldtype": "Currency",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Cumulative Transaction Threshold"
|
||||
},
|
||||
@ -59,7 +59,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-31 11:42:12.213977",
|
||||
"modified": "2022-01-13 12:04:42.904263",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Withholding Rate",
|
||||
@ -68,5 +68,6 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
@ -185,8 +185,6 @@ class AccountsController(TransactionBase):
|
||||
frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx))
|
||||
elif getdate(self.posting_date) > getdate(d.service_end_date):
|
||||
frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx))
|
||||
elif getdate(self.posting_date) > getdate(d.service_start_date):
|
||||
frappe.throw(_("Row #{0}: Service Start Date cannot be before Invoice Posting Date").format(d.idx))
|
||||
|
||||
def validate_invoice_documents_schedule(self):
|
||||
self.validate_payment_schedule_dates()
|
||||
|
@ -1,294 +1,108 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "HR-LPR-.YYYY.-.#####",
|
||||
"beta": 0,
|
||||
"creation": "2018-04-13 15:20:52.864288",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"from_date",
|
||||
"to_date",
|
||||
"is_active",
|
||||
"column_break_3",
|
||||
"company",
|
||||
"optional_holiday_list"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "from_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_standard_filter": 0,
|
||||
"label": "From Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "to_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_standard_filter": 0,
|
||||
"label": "To Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "is_active",
|
||||
"fieldtype": "Check",
|
||||
"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": "Is Active",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Is Active"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "optional_holiday_list",
|
||||
"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": "Holiday List for Optional Leave",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Holiday List",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"options": "Holiday List"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-05-30 16:15:43.305502",
|
||||
"links": [],
|
||||
"modified": "2022-01-13 13:28:12.951025",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Period",
|
||||
"name_case": "",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"search_fields": "from_date, to_date, company",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
@ -113,10 +113,11 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-01 17:54:01.014509",
|
||||
"modified": "2022-01-13 13:37:11.218882",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Policy Assignment",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@ -164,5 +165,7 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "employee_name",
|
||||
"track_changes": 1
|
||||
}
|
@ -48,7 +48,16 @@ frappe.listview_settings['Leave Policy Assignment'] = {
|
||||
if (cur_dialog.fields_dict.leave_period.value) {
|
||||
me.set_effective_date();
|
||||
}
|
||||
}
|
||||
},
|
||||
get_query() {
|
||||
let filters = {"is_active": 1};
|
||||
if (cur_dialog.fields_dict.company.value)
|
||||
filters["company"] = cur_dialog.fields_dict.company.value;
|
||||
|
||||
return {
|
||||
filters: filters
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldtype: "Column Break"
|
||||
|
@ -279,7 +279,7 @@ erpnext.patches.v13_0.add_custom_field_for_south_africa #2
|
||||
erpnext.patches.v13_0.update_recipient_email_digest
|
||||
erpnext.patches.v13_0.shopify_deprecation_warning
|
||||
erpnext.patches.v13_0.remove_bad_selling_defaults
|
||||
erpnext.patches.v13_0.trim_whitespace_from_serial_nos
|
||||
erpnext.patches.v13_0.trim_whitespace_from_serial_nos # 16-01-2022
|
||||
erpnext.patches.v13_0.migrate_stripe_api
|
||||
erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
|
||||
erpnext.patches.v13_0.einvoicing_deprecation_warning
|
||||
@ -313,8 +313,8 @@ erpnext.patches.v14_0.delete_healthcare_doctypes
|
||||
erpnext.patches.v13_0.update_category_in_ltds_certificate
|
||||
erpnext.patches.v13_0.create_pan_field_for_india #2
|
||||
erpnext.patches.v14_0.delete_hub_doctypes
|
||||
erpnext.patches.v13_0.create_ksa_vat_custom_fields
|
||||
erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit
|
||||
erpnext.patches.v13_0.create_ksa_vat_custom_fields # 07-01-2022
|
||||
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
|
||||
erpnext.patches.v14_0.migrate_crm_settings
|
||||
erpnext.patches.v13_0.rename_ksa_qr_field
|
||||
@ -326,3 +326,4 @@ erpnext.patches.v14_0.set_payroll_cost_centers
|
||||
erpnext.patches.v13_0.agriculture_deprecation_warning
|
||||
erpnext.patches.v14_0.delete_agriculture_doctypes
|
||||
erpnext.patches.v13_0.update_exchange_rate_settings
|
||||
erpnext.patches.v14_0.rearrange_company_fields
|
||||
|
@ -9,13 +9,15 @@ def execute():
|
||||
from `tabStock Ledger Entry`
|
||||
where
|
||||
is_cancelled = 0
|
||||
and (serial_no like %s or serial_no like %s or serial_no like %s or serial_no like %s)
|
||||
and ( serial_no like %s or serial_no like %s or serial_no like %s or serial_no like %s
|
||||
or serial_no = %s )
|
||||
""",
|
||||
(
|
||||
" %", # leading whitespace
|
||||
"% ", # trailing whitespace
|
||||
"%\n %", # leading whitespace on newline
|
||||
"% \n%", # trailing whitespace on newline
|
||||
"\n", # just new line
|
||||
),
|
||||
as_dict=True,
|
||||
)
|
||||
|
@ -47,3 +47,18 @@ def execute():
|
||||
frappe.delete_doc("DocType", doctype, ignore_missing=True)
|
||||
|
||||
frappe.delete_doc("Module Def", "Healthcare", ignore_missing=True, force=True)
|
||||
|
||||
custom_fields = {
|
||||
'Sales Invoice': ['patient', 'patient_name', 'ref_practitioner'],
|
||||
'Sales Invoice Item': ['reference_dt', 'reference_dn'],
|
||||
'Stock Entry': ['inpatient_medication_entry'],
|
||||
'Stock Entry Detail': ['patient', 'inpatient_medication_entry_child'],
|
||||
}
|
||||
for doc, fields in custom_fields.items():
|
||||
filters = {
|
||||
'dt': doc,
|
||||
'fieldname': ['in', fields]
|
||||
}
|
||||
records = frappe.get_all('Custom Field', filters=filters, pluck='name')
|
||||
for record in records:
|
||||
frappe.delete_doc('Custom Field', record, ignore_missing=True, force=True)
|
||||
|
31
erpnext/patches/v14_0/rearrange_company_fields.py
Normal file
31
erpnext/patches/v14_0/rearrange_company_fields.py
Normal file
@ -0,0 +1,31 @@
|
||||
import frappe
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('setup', 'doctype', 'company')
|
||||
|
||||
custom_fields = {
|
||||
'Company': [
|
||||
dict(fieldname='hra_section', label='HRA Settings',
|
||||
fieldtype='Section Break', insert_after='asset_received_but_not_billed', collapsible=1),
|
||||
dict(fieldname='basic_component', label='Basic Component',
|
||||
fieldtype='Link', options='Salary Component', insert_after='hra_section'),
|
||||
dict(fieldname='hra_component', label='HRA Component',
|
||||
fieldtype='Link', options='Salary Component', insert_after='basic_component'),
|
||||
dict(fieldname='hra_column_break', fieldtype='Column Break', insert_after='hra_component'),
|
||||
dict(fieldname='arrear_component', label='Arrear Component',
|
||||
fieldtype='Link', options='Salary Component', insert_after='hra_column_break'),
|
||||
dict(fieldname='non_profit_section', label='Non Profit Settings',
|
||||
fieldtype='Section Break', insert_after='arrear_component', collapsible=1),
|
||||
dict(fieldname='company_80g_number', label='80G Number',
|
||||
fieldtype='Data', insert_after='non_profit_section'),
|
||||
dict(fieldname='with_effect_from', label='80G With Effect From',
|
||||
fieldtype='Date', insert_after='company_80g_number'),
|
||||
dict(fieldname='non_profit_column_break', fieldtype='Column Break', insert_after='with_effect_from'),
|
||||
dict(fieldname='pan_details', label='PAN Number',
|
||||
fieldtype='Data', insert_after='non_profit_column_break')
|
||||
]
|
||||
}
|
||||
|
||||
create_custom_fields(custom_fields, update=True)
|
@ -61,6 +61,8 @@ class PayrollEntry(Document):
|
||||
def on_cancel(self):
|
||||
frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
|
||||
where payroll_entry=%s """, (self.name)))
|
||||
self.db_set("salary_slips_created", 0)
|
||||
self.db_set("salary_slips_submitted", 0)
|
||||
|
||||
def get_emp_list(self):
|
||||
"""
|
||||
|
@ -567,16 +567,16 @@ def get_custom_fields():
|
||||
fieldtype='Link', options='Salary Component', insert_after='basic_component'),
|
||||
dict(fieldname='hra_column_break', fieldtype='Column Break', insert_after='hra_component'),
|
||||
dict(fieldname='arrear_component', label='Arrear Component',
|
||||
fieldtype='Link', options='Salary Component', insert_after='hra_component'),
|
||||
fieldtype='Link', options='Salary Component', insert_after='hra_column_break'),
|
||||
dict(fieldname='non_profit_section', label='Non Profit Settings',
|
||||
fieldtype='Section Break', insert_after='asset_received_but_not_billed', collapsible=1),
|
||||
fieldtype='Section Break', insert_after='arrear_component', collapsible=1),
|
||||
dict(fieldname='company_80g_number', label='80G Number',
|
||||
fieldtype='Data', insert_after='non_profit_section'),
|
||||
dict(fieldname='with_effect_from', label='80G With Effect From',
|
||||
fieldtype='Date', insert_after='company_80g_number'),
|
||||
dict(fieldname='non_profit_column_break', fieldtype='Column Break', insert_after='with_effect_from'),
|
||||
dict(fieldname='pan_details', label='PAN Number',
|
||||
fieldtype='Data', insert_after='with_effect_from')
|
||||
fieldtype='Data', insert_after='non_profit_column_break')
|
||||
],
|
||||
'Employee Tax Exemption Declaration':[
|
||||
dict(fieldname='hra_section', label='HRA Exemption',
|
||||
|
@ -53,7 +53,8 @@ frappe.query_reports["GSTR-1"] = {
|
||||
{ "value": "CDNR-REG", "label": __("Credit/Debit Notes (Registered) - 9B") },
|
||||
{ "value": "CDNR-UNREG", "label": __("Credit/Debit Notes (Unregistered) - 9B") },
|
||||
{ "value": "EXPORT", "label": __("Export Invoice - 6A") },
|
||||
{ "value": "Advances", "label": __("Tax Liability (Advances Received) - 11A(1), 11A(2)") }
|
||||
{ "value": "Advances", "label": __("Tax Liability (Advances Received) - 11A(1), 11A(2)") },
|
||||
{ "value": "NIL Rated", "label": __("NIL RATED/EXEMPTED Invoices") }
|
||||
],
|
||||
"default": "B2B"
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ class Gstr1Report(object):
|
||||
port_code,
|
||||
shipping_bill_number,
|
||||
shipping_bill_date,
|
||||
reason_for_issuing_document
|
||||
reason_for_issuing_document,
|
||||
company_gstin
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
@ -62,6 +63,8 @@ class Gstr1Report(object):
|
||||
self.get_b2c_data()
|
||||
elif self.filters.get("type_of_business") == "Advances":
|
||||
self.get_advance_data()
|
||||
elif self.filters.get("type_of_business") == "NIL Rated":
|
||||
self.get_nil_rated_invoices()
|
||||
elif self.invoices:
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
invoice_details = self.invoices.get(inv)
|
||||
@ -91,6 +94,57 @@ class Gstr1Report(object):
|
||||
row= [key[0], key[1], value[0], value[1]]
|
||||
self.data.append(row)
|
||||
|
||||
def get_nil_rated_invoices(self):
|
||||
nil_exempt_output = [
|
||||
{
|
||||
"description": "Inter-State supplies to registered persons",
|
||||
"nil_rated": 0.0,
|
||||
"exempted": 0.0,
|
||||
"non_gst": 0.0
|
||||
},
|
||||
{
|
||||
"description": "Intra-State supplies to registered persons",
|
||||
"nil_rated": 0.0,
|
||||
"exempted": 0.0,
|
||||
"non_gst": 0.0
|
||||
},
|
||||
{
|
||||
"description": "Inter-State supplies to unregistered persons",
|
||||
"nil_rated": 0.0,
|
||||
"exempted": 0.0,
|
||||
"non_gst": 0.0
|
||||
},
|
||||
{
|
||||
"description": "Intra-State supplies to unregistered persons",
|
||||
"nil_rated": 0.0,
|
||||
"exempted": 0.0,
|
||||
"non_gst": 0.0
|
||||
}
|
||||
]
|
||||
|
||||
for invoice, details in self.nil_exempt_non_gst.items():
|
||||
invoice_detail = self.invoices.get(invoice)
|
||||
if invoice_detail.get('gst_category') in ("Registered Regular", "Deemed Export", "SEZ"):
|
||||
if is_inter_state(invoice_detail):
|
||||
nil_exempt_output[0]["nil_rated"] += details[0]
|
||||
nil_exempt_output[0]["exempted"] += details[1]
|
||||
nil_exempt_output[0]["non_gst"] += details[2]
|
||||
else:
|
||||
nil_exempt_output[1]["nil_rated"] += details[0]
|
||||
nil_exempt_output[1]["exempted"] += details[1]
|
||||
nil_exempt_output[1]["non_gst"] += details[2]
|
||||
else:
|
||||
if is_inter_state(invoice_detail):
|
||||
nil_exempt_output[2]["nil_rated"] += details[0]
|
||||
nil_exempt_output[2]["exempted"] += details[1]
|
||||
nil_exempt_output[2]["non_gst"] += details[2]
|
||||
else:
|
||||
nil_exempt_output[3]["nil_rated"] += details[0]
|
||||
nil_exempt_output[3]["exempted"] += details[1]
|
||||
nil_exempt_output[3]["non_gst"] += details[2]
|
||||
|
||||
self.data = nil_exempt_output
|
||||
|
||||
def get_b2c_data(self):
|
||||
b2cs_output = {}
|
||||
|
||||
@ -240,10 +294,11 @@ class Gstr1Report(object):
|
||||
def get_invoice_items(self):
|
||||
self.invoice_items = frappe._dict()
|
||||
self.item_tax_rate = frappe._dict()
|
||||
self.nil_exempt_non_gst = {}
|
||||
|
||||
items = frappe.db.sql("""
|
||||
select item_code, parent, taxable_value, base_net_amount, item_tax_rate
|
||||
from `tab%s Item`
|
||||
select item_code, parent, taxable_value, base_net_amount, item_tax_rate, is_nil_exempt,
|
||||
is_non_gst from `tab%s Item`
|
||||
where parent in (%s)
|
||||
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
|
||||
|
||||
@ -260,6 +315,16 @@ class Gstr1Report(object):
|
||||
tax_rate_dict = self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, [])
|
||||
tax_rate_dict.append(rate)
|
||||
|
||||
if d.is_nil_exempt:
|
||||
self.nil_exempt_non_gst.setdefault(d.parent, [0.0, 0.0, 0.0])
|
||||
if item_tax_rate:
|
||||
self.nil_exempt_non_gst[d.parent][0] += d.get('taxable_value', 0)
|
||||
else:
|
||||
self.nil_exempt_non_gst[d.parent][1] += d.get('taxable_value', 0)
|
||||
elif d.is_non_gst:
|
||||
self.nil_exempt_non_gst.setdefault(d.parent, [0.0, 0.0, 0.0])
|
||||
self.nil_exempt_non_gst[d.parent][2] += d.get('taxable_value', 0)
|
||||
|
||||
def get_items_based_on_tax_rate(self):
|
||||
self.tax_details = frappe.db.sql("""
|
||||
select
|
||||
@ -322,21 +387,24 @@ class Gstr1Report(object):
|
||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
||||
|
||||
def get_columns(self):
|
||||
self.tax_columns = [
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Int",
|
||||
"width": 60
|
||||
},
|
||||
{
|
||||
"fieldname": "taxable_value",
|
||||
"label": "Taxable Value",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
self.other_columns = []
|
||||
self.tax_columns = []
|
||||
|
||||
if self.filters.get("type_of_business") != "NIL Rated":
|
||||
self.tax_columns = [
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Int",
|
||||
"width": 60
|
||||
},
|
||||
{
|
||||
"fieldname": "taxable_value",
|
||||
"label": "Taxable Value",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
if self.filters.get("type_of_business") == "B2B":
|
||||
self.invoice_columns = [
|
||||
@ -705,6 +773,33 @@ class Gstr1Report(object):
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
elif self.filters.get("type_of_business") == "NIL Rated":
|
||||
self.invoice_columns = [
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Data",
|
||||
"width": 420
|
||||
},
|
||||
{
|
||||
"fieldname": "nil_rated",
|
||||
"label": "Nil Rated",
|
||||
"fieldtype": "Currency",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"fieldname": "exempted",
|
||||
"label": "Exempted",
|
||||
"fieldtype": "Currency",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"fieldname": "non_gst",
|
||||
"label": "Non GST",
|
||||
"fieldtype": "Currency",
|
||||
"width": 200
|
||||
}
|
||||
]
|
||||
|
||||
self.columns = self.invoice_columns + self.tax_columns + self.other_columns
|
||||
|
||||
@ -768,6 +863,11 @@ def get_json(filters, report_name, data):
|
||||
out = get_advances_json(res, gstin)
|
||||
gst_json["at"] = out
|
||||
|
||||
elif filters["type_of_business"] == "NIL Rated":
|
||||
res = report_data[:-1]
|
||||
out = get_exempted_json(res)
|
||||
gst_json["nil"] = out
|
||||
|
||||
return {
|
||||
'report_name': report_name,
|
||||
'report_type': filters['type_of_business'],
|
||||
@ -980,6 +1080,36 @@ def get_cdnr_unreg_json(res, gstin):
|
||||
|
||||
return out
|
||||
|
||||
def get_exempted_json(data):
|
||||
out = {
|
||||
"inv": [
|
||||
{
|
||||
"sply_ty": "INTRB2B"
|
||||
},
|
||||
{
|
||||
"sply_ty": "INTRAB2B"
|
||||
},
|
||||
{
|
||||
"sply_ty": "INTRB2C"
|
||||
},
|
||||
{
|
||||
"sply_ty": "INTRAB2C"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
for i, v in enumerate(data):
|
||||
if data[i].get('nil_rated'):
|
||||
out['inv'][i]['nil_amt'] = data[i]['nil_rated']
|
||||
|
||||
if data[i].get('exempted'):
|
||||
out['inv'][i]['expt_amt'] = data[i]['exempted']
|
||||
|
||||
if data[i].get('non_gst'):
|
||||
out['inv'][i]['ngsup_amt'] = data[i]['non_gst']
|
||||
|
||||
return out
|
||||
|
||||
def get_invoice_type_for_cdnr(row):
|
||||
if row.get('gst_category') == 'SEZ':
|
||||
if row.get('export_type') == 'WPAY':
|
||||
@ -1064,3 +1194,9 @@ def download_json_file():
|
||||
frappe.response['filecontent'] = data['data']
|
||||
frappe.response['content_type'] = 'application/json'
|
||||
frappe.response['type'] = 'download'
|
||||
|
||||
def is_inter_state(invoice_detail):
|
||||
if invoice_detail.place_of_supply.split("-")[0] != invoice_detail.company_gstin[:2]:
|
||||
return True
|
||||
else:
|
||||
return False
|
@ -3,12 +3,10 @@
|
||||
|
||||
import frappe
|
||||
from frappe.permissions import add_permission, update_permission_property
|
||||
from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields
|
||||
from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
|
||||
def setup(company=None, patch=True):
|
||||
uae_custom_fields()
|
||||
add_print_formats()
|
||||
add_permissions()
|
||||
make_custom_fields()
|
||||
@ -40,38 +38,67 @@ def make_custom_fields():
|
||||
- Company Name in Arabic
|
||||
- Address in Arabic
|
||||
"""
|
||||
is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
|
||||
fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description',
|
||||
print_hide=1)
|
||||
|
||||
is_exempt = dict(fieldname='is_exempt', label='Is Exempt',
|
||||
fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated',
|
||||
print_hide=1)
|
||||
|
||||
purchase_invoice_fields = [
|
||||
dict(fieldname='company_trn', label='Company TRN',
|
||||
fieldtype='Read Only', insert_after='shipping_address',
|
||||
fetch_from='company.tax_id', print_hide=1),
|
||||
dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
|
||||
fieldtype='Read Only', insert_after='supplier_name',
|
||||
fetch_from='supplier.supplier_name_in_arabic', print_hide=1)
|
||||
]
|
||||
|
||||
sales_invoice_fields = [
|
||||
dict(fieldname='company_trn', label='Company TRN',
|
||||
fieldtype='Read Only', insert_after='company_address',
|
||||
fetch_from='company.tax_id', print_hide=1),
|
||||
dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
|
||||
fieldtype='Read Only', insert_after='customer_name',
|
||||
fetch_from='customer.customer_name_in_arabic', print_hide=1),
|
||||
dict(fieldname='ksa_einv_qr', label='KSA E-Invoicing QR',
|
||||
fieldtype='Attach Image', read_only=1, no_copy=1, hidden=1)
|
||||
]
|
||||
|
||||
custom_fields = {
|
||||
'Sales Invoice': [
|
||||
dict(
|
||||
fieldname='ksa_einv_qr',
|
||||
label='KSA E-Invoicing QR',
|
||||
fieldtype='Attach Image',
|
||||
read_only=1, no_copy=1, hidden=1
|
||||
)
|
||||
'Item': [is_zero_rated, is_exempt],
|
||||
'Customer': [
|
||||
dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
|
||||
fieldtype='Data', insert_after='customer_name'),
|
||||
],
|
||||
'POS Invoice': [
|
||||
dict(
|
||||
fieldname='ksa_einv_qr',
|
||||
label='KSA E-Invoicing QR',
|
||||
fieldtype='Attach Image',
|
||||
read_only=1, no_copy=1, hidden=1
|
||||
)
|
||||
'Supplier': [
|
||||
dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
|
||||
fieldtype='Data', insert_after='supplier_name'),
|
||||
],
|
||||
'Purchase Invoice': purchase_invoice_fields,
|
||||
'Purchase Order': purchase_invoice_fields,
|
||||
'Purchase Receipt': purchase_invoice_fields,
|
||||
'Sales Invoice': sales_invoice_fields,
|
||||
'POS Invoice': sales_invoice_fields,
|
||||
'Sales Order': sales_invoice_fields,
|
||||
'Delivery Note': sales_invoice_fields,
|
||||
'Sales Invoice Item': [is_zero_rated, is_exempt],
|
||||
'POS Invoice Item': [is_zero_rated, is_exempt],
|
||||
'Purchase Invoice Item': [is_zero_rated, is_exempt],
|
||||
'Sales Order Item': [is_zero_rated, is_exempt],
|
||||
'Delivery Note Item': [is_zero_rated, is_exempt],
|
||||
'Quotation Item': [is_zero_rated, is_exempt],
|
||||
'Purchase Order Item': [is_zero_rated, is_exempt],
|
||||
'Purchase Receipt Item': [is_zero_rated, is_exempt],
|
||||
'Supplier Quotation Item': [is_zero_rated, is_exempt],
|
||||
'Address': [
|
||||
dict(
|
||||
fieldname='address_in_arabic',
|
||||
label='Address in Arabic',
|
||||
fieldtype='Data',
|
||||
insert_after='address_line2'
|
||||
)
|
||||
dict(fieldname='address_in_arabic', label='Address in Arabic',
|
||||
fieldtype='Data',insert_after='address_line2')
|
||||
],
|
||||
'Company': [
|
||||
dict(
|
||||
fieldname='company_name_in_arabic',
|
||||
label='Company Name In Arabic',
|
||||
fieldtype='Data',
|
||||
insert_after='company_name'
|
||||
)
|
||||
dict(fieldname='company_name_in_arabic', label='Company Name In Arabic',
|
||||
fieldtype='Data', insert_after='company_name')
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -213,6 +213,9 @@ erpnext.company.setup_queries = function(frm) {
|
||||
["default_payroll_payable_account", {"root_type": "Liability"}],
|
||||
["round_off_account", {"root_type": "Expense"}],
|
||||
["write_off_account", {"root_type": "Expense"}],
|
||||
["default_deferred_expense_account", {}],
|
||||
["default_deferred_revenue_account", {}],
|
||||
["default_expense_claim_payable_account", {}],
|
||||
["default_discount_account", {}],
|
||||
["discount_allowed_account", {"root_type": "Expense"}],
|
||||
["discount_received_account", {"root_type": "Income"}],
|
||||
|
@ -292,6 +292,7 @@ def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):
|
||||
join `tabStock Ledger Entry` ignore index (item_code, warehouse)
|
||||
on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )
|
||||
where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s
|
||||
and `tabStock Ledger Entry`.is_cancelled = 0
|
||||
and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0}
|
||||
group by batch_id
|
||||
order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
|
||||
@ -336,4 +337,4 @@ def get_pos_reserved_batch_qty(filters):
|
||||
).run()
|
||||
|
||||
flt_reserved_batch_qty = flt(reserved_batch_qty[0][0])
|
||||
return flt_reserved_batch_qty
|
||||
return flt_reserved_batch_qty
|
||||
|
@ -402,10 +402,16 @@ def update_serial_nos(sle, item_det):
|
||||
def get_auto_serial_nos(serial_no_series, qty):
|
||||
serial_nos = []
|
||||
for i in range(cint(qty)):
|
||||
serial_nos.append(make_autoname(serial_no_series, "Serial No"))
|
||||
serial_nos.append(get_new_serial_number(serial_no_series))
|
||||
|
||||
return "\n".join(serial_nos)
|
||||
|
||||
def get_new_serial_number(series):
|
||||
sr_no = make_autoname(series, "Serial No")
|
||||
if frappe.db.exists("Serial No", sr_no):
|
||||
sr_no = get_new_serial_number(series)
|
||||
return sr_no
|
||||
|
||||
def auto_make_serial_nos(args):
|
||||
serial_nos = get_serial_nos(args.get('serial_no'))
|
||||
created_numbers = []
|
||||
|
@ -8,6 +8,7 @@
|
||||
import frappe
|
||||
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
@ -176,6 +177,24 @@ class TestSerialNo(ERPNextTestCase):
|
||||
self.assertEqual(sn_doc.warehouse, "_Test Warehouse - _TC")
|
||||
self.assertEqual(sn_doc.purchase_document_no, se.name)
|
||||
|
||||
def test_auto_creation_of_serial_no(self):
|
||||
"""
|
||||
Test if auto created Serial No excludes existing serial numbers
|
||||
"""
|
||||
item_code = make_item("_Test Auto Serial Item ", {
|
||||
"has_serial_no": 1,
|
||||
"serial_no_series": "XYZ.###"
|
||||
}).item_code
|
||||
|
||||
# Reserve XYZ005
|
||||
pr_1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no="XYZ005")
|
||||
# XYZ005 is already used and will throw an error if used again
|
||||
pr_2 = make_purchase_receipt(item_code=item_code, qty=10)
|
||||
|
||||
self.assertEqual(get_serial_nos(pr_1.get("items")[0].serial_no)[0], "XYZ005")
|
||||
for serial_no in get_serial_nos(pr_2.get("items")[0].serial_no):
|
||||
self.assertNotEqual(serial_no, "XYZ005")
|
||||
|
||||
def test_serial_no_sanitation(self):
|
||||
"Test if Serial No input is sanitised before entering the DB."
|
||||
item_code = "_Test Serialized Item"
|
||||
|
@ -86,8 +86,11 @@ class StockEntry(StockController):
|
||||
self.validate_warehouse()
|
||||
self.validate_work_order()
|
||||
self.validate_bom()
|
||||
self.mark_finished_and_scrap_items()
|
||||
self.validate_finished_goods()
|
||||
|
||||
if self.purpose in ("Manufacture", "Repack"):
|
||||
self.mark_finished_and_scrap_items()
|
||||
self.validate_finished_goods()
|
||||
|
||||
self.validate_with_material_request()
|
||||
self.validate_batch()
|
||||
self.validate_inspection()
|
||||
@ -706,26 +709,25 @@ class StockEntry(StockController):
|
||||
validate_bom_no(item_code, d.bom_no)
|
||||
|
||||
def mark_finished_and_scrap_items(self):
|
||||
if self.purpose in ("Repack", "Manufacture"):
|
||||
if any([d.item_code for d in self.items if (d.is_finished_item and d.t_warehouse)]):
|
||||
return
|
||||
if any([d.item_code for d in self.items if (d.is_finished_item and d.t_warehouse)]):
|
||||
return
|
||||
|
||||
finished_item = self.get_finished_item()
|
||||
finished_item = self.get_finished_item()
|
||||
|
||||
if not finished_item and self.purpose == "Manufacture":
|
||||
# In case of independent Manufacture entry, don't auto set
|
||||
# user must decide and set
|
||||
return
|
||||
if not finished_item and self.purpose == "Manufacture":
|
||||
# In case of independent Manufacture entry, don't auto set
|
||||
# user must decide and set
|
||||
return
|
||||
|
||||
for d in self.items:
|
||||
if d.t_warehouse and not d.s_warehouse:
|
||||
if self.purpose=="Repack" or d.item_code == finished_item:
|
||||
d.is_finished_item = 1
|
||||
else:
|
||||
d.is_scrap_item = 1
|
||||
for d in self.items:
|
||||
if d.t_warehouse and not d.s_warehouse:
|
||||
if self.purpose=="Repack" or d.item_code == finished_item:
|
||||
d.is_finished_item = 1
|
||||
else:
|
||||
d.is_finished_item = 0
|
||||
d.is_scrap_item = 0
|
||||
d.is_scrap_item = 1
|
||||
else:
|
||||
d.is_finished_item = 0
|
||||
d.is_scrap_item = 0
|
||||
|
||||
def get_finished_item(self):
|
||||
finished_item = None
|
||||
@ -738,9 +740,9 @@ class StockEntry(StockController):
|
||||
|
||||
def validate_finished_goods(self):
|
||||
"""
|
||||
1. Check if FG exists
|
||||
2. Check if Multiple FG Items are present
|
||||
3. Check FG Item and Qty against WO if present
|
||||
1. Check if FG exists (mfg, repack)
|
||||
2. Check if Multiple FG Items are present (mfg)
|
||||
3. Check FG Item and Qty against WO if present (mfg)
|
||||
"""
|
||||
production_item, wo_qty, finished_items = None, 0, []
|
||||
|
||||
@ -753,8 +755,9 @@ class StockEntry(StockController):
|
||||
for d in self.get('items'):
|
||||
if d.is_finished_item:
|
||||
if not self.work_order:
|
||||
# Independent MFG Entry/ Repack Entry, no WO to match against
|
||||
finished_items.append(d.item_code)
|
||||
continue # Independent Manufacture Entry, no WO to match against
|
||||
continue
|
||||
|
||||
if d.item_code != production_item:
|
||||
frappe.throw(_("Finished Item {0} does not match with Work Order {1}")
|
||||
@ -767,19 +770,17 @@ class StockEntry(StockController):
|
||||
|
||||
finished_items.append(d.item_code)
|
||||
|
||||
if len(set(finished_items)) > 1:
|
||||
if not finished_items:
|
||||
frappe.throw(
|
||||
msg=_("Multiple items cannot be marked as finished item"),
|
||||
title=_("Note"),
|
||||
exc=FinishedGoodError
|
||||
msg=_("There must be atleast 1 Finished Good in this Stock Entry").format(self.name),
|
||||
title=_("Missing Finished Good"), exc=FinishedGoodError
|
||||
)
|
||||
|
||||
if self.purpose == "Manufacture":
|
||||
if not finished_items:
|
||||
if len(set(finished_items)) > 1:
|
||||
frappe.throw(
|
||||
msg=_("There must be atleast 1 Finished Good in this Stock Entry").format(self.name),
|
||||
title=_("Missing Finished Good"),
|
||||
exc=FinishedGoodError
|
||||
msg=_("Multiple items cannot be marked as finished item"),
|
||||
title=_("Note"), exc=FinishedGoodError
|
||||
)
|
||||
|
||||
allowance_percentage = flt(
|
||||
|
@ -226,9 +226,47 @@ class TestStockEntry(ERPNextTestCase):
|
||||
|
||||
mtn.cancel()
|
||||
|
||||
def test_repack_no_change_in_valuation(self):
|
||||
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
|
||||
def test_repack_multiple_fg(self):
|
||||
"Test `is_finished_item` for one item repacked into two items."
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=100, basic_rate=100)
|
||||
|
||||
repack = frappe.copy_doc(test_records[3])
|
||||
repack.posting_date = nowdate()
|
||||
repack.posting_time = nowtime()
|
||||
|
||||
repack.items[0].qty = 100.0
|
||||
repack.items[0].transfer_qty = 100.0
|
||||
repack.items[1].qty = 50.0
|
||||
|
||||
repack.append("items", {
|
||||
"conversion_factor": 1.0,
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Stock Entry Detail",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"basic_rate": 150,
|
||||
"item_code": "_Test Item 2",
|
||||
"parentfield": "items",
|
||||
"qty": 50.0,
|
||||
"stock_uom": "_Test UOM",
|
||||
"t_warehouse": "_Test Warehouse - _TC",
|
||||
"transfer_qty": 50.0,
|
||||
"uom": "_Test UOM"
|
||||
})
|
||||
repack.set_stock_entry_type()
|
||||
repack.insert()
|
||||
|
||||
self.assertEqual(repack.items[1].is_finished_item, 1)
|
||||
self.assertEqual(repack.items[2].is_finished_item, 1)
|
||||
|
||||
repack.items[1].is_finished_item = 0
|
||||
repack.items[2].is_finished_item = 0
|
||||
|
||||
# must raise error if 0 fg in repack entry
|
||||
self.assertRaises(FinishedGoodError, repack.validate_finished_goods)
|
||||
|
||||
repack.delete() # teardown
|
||||
|
||||
def test_repack_no_change_in_valuation(self):
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
|
||||
qty=50, basic_rate=100)
|
||||
|
@ -55,7 +55,8 @@ def get_stock_ledger_entries(filters):
|
||||
return frappe.db.sql("""select item_code, batch_no, warehouse,
|
||||
posting_date, actual_qty
|
||||
from `tabStock Ledger Entry`
|
||||
where docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse""" %
|
||||
where is_cancelled = 0
|
||||
and docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse""" %
|
||||
conditions, as_dict=1)
|
||||
|
||||
def get_item_warehouse_batch_map(filters, float_precision):
|
||||
|
@ -91,7 +91,7 @@ def get_stock_value_difference_list(filtered_entries: FilteredEntries) -> SVDLis
|
||||
voucher_nos = [fe.get('voucher_no') for fe in filtered_entries]
|
||||
svd_list = frappe.get_list(
|
||||
'Stock Ledger Entry', fields=['item_code','stock_value_difference'],
|
||||
filters=[('voucher_no', 'in', voucher_nos)]
|
||||
filters=[('voucher_no', 'in', voucher_nos), ("is_cancelled", "=", 0)]
|
||||
)
|
||||
assign_item_groups_to_svd_list(svd_list)
|
||||
return svd_list
|
||||
|
@ -76,6 +76,7 @@ def get_consumed_items(condition):
|
||||
on sle.voucher_no = se.name
|
||||
where
|
||||
actual_qty < 0
|
||||
and is_cancelled = 0
|
||||
and voucher_type not in ('Delivery Note', 'Sales Invoice')
|
||||
%s
|
||||
group by item_code""" % condition, as_dict=1)
|
||||
|
@ -105,6 +105,7 @@ def get_args_for_future_sle(row):
|
||||
|
||||
def validate_serial_no(sle):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
for sn in get_serial_nos(sle.serial_no):
|
||||
args = copy.deepcopy(sle)
|
||||
args.serial_no = sn
|
||||
@ -423,6 +424,8 @@ class update_entries_after(object):
|
||||
return sorted(entries_to_fix, key=lambda k: k['timestamp'])
|
||||
|
||||
def process_sle(self, sle):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
# previous sle data for this warehouse
|
||||
self.wh_data = self.data[sle.warehouse]
|
||||
|
||||
@ -437,7 +440,7 @@ class update_entries_after(object):
|
||||
if not self.args.get("sle_id"):
|
||||
self.get_dynamic_incoming_outgoing_rate(sle)
|
||||
|
||||
if sle.serial_no:
|
||||
if get_serial_nos(sle.serial_no):
|
||||
self.get_serialized_values(sle)
|
||||
self.wh_data.qty_after_transaction += flt(sle.actual_qty)
|
||||
if sle.voucher_type == "Stock Reconciliation":
|
||||
@ -449,8 +452,9 @@ class update_entries_after(object):
|
||||
# assert
|
||||
self.wh_data.valuation_rate = sle.valuation_rate
|
||||
self.wh_data.qty_after_transaction = sle.qty_after_transaction
|
||||
self.wh_data.stock_queue = [[self.wh_data.qty_after_transaction, self.wh_data.valuation_rate]]
|
||||
self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
|
||||
if self.valuation_method != "Moving Average":
|
||||
self.wh_data.stock_queue = [[self.wh_data.qty_after_transaction, self.wh_data.valuation_rate]]
|
||||
else:
|
||||
if self.valuation_method == "Moving Average":
|
||||
self.get_moving_average_values(sle)
|
||||
@ -646,6 +650,7 @@ class update_entries_after(object):
|
||||
where
|
||||
company = %s
|
||||
and actual_qty > 0
|
||||
and is_cancelled = 0
|
||||
and (serial_no = %s
|
||||
or serial_no like %s
|
||||
or serial_no like %s
|
||||
@ -901,6 +906,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
||||
item_code = %s
|
||||
AND warehouse = %s
|
||||
AND valuation_rate >= 0
|
||||
AND is_cancelled = 0
|
||||
AND NOT (voucher_no = %s AND voucher_type = %s)
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type))
|
||||
|
||||
@ -911,6 +917,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
||||
where
|
||||
item_code = %s
|
||||
AND valuation_rate > 0
|
||||
AND is_cancelled = 0
|
||||
AND NOT(voucher_no = %s AND voucher_type = %s)
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, voucher_no, voucher_type))
|
||||
|
||||
|
@ -111,6 +111,7 @@ frappe.ui.form.on('Service Level Agreement', {
|
||||
filters: [
|
||||
['DocType', 'issingle', '=', 0],
|
||||
['DocType', 'istable', '=', 0],
|
||||
['DocType', 'is_submittable', '=', 0],
|
||||
['DocType', 'name', 'not in', invalid_doctypes],
|
||||
['DocType', 'module', 'not in', ["Email", "Core", "Custom", "Event Streaming", "Social", "Data Migration", "Geo", "Desk"]]
|
||||
]
|
||||
|
@ -29,6 +29,7 @@ from erpnext.support.doctype.issue.issue import get_holidays
|
||||
|
||||
class ServiceLevelAgreement(Document):
|
||||
def validate(self):
|
||||
self.validate_selected_doctype()
|
||||
self.validate_doc()
|
||||
self.validate_status_field()
|
||||
self.check_priorities()
|
||||
@ -106,6 +107,23 @@ class ServiceLevelAgreement(Document):
|
||||
frappe.throw(_("Service Level Agreement for {0} {1} already exists.").format(
|
||||
frappe.bold(self.entity_type), frappe.bold(self.entity)))
|
||||
|
||||
def validate_selected_doctype(self):
|
||||
invalid_doctypes = list(frappe.model.core_doctypes_list)
|
||||
invalid_doctypes.extend(['Cost Center', 'Company'])
|
||||
valid_document_types = frappe.get_all('DocType', {
|
||||
'issingle': 0,
|
||||
'istable': 0,
|
||||
'is_submittable': 0,
|
||||
'name': ['not in', invalid_doctypes],
|
||||
'module': ['not in', ["Email", "Core", "Custom", "Event Streaming", "Social", "Data Migration", "Geo", "Desk"]]
|
||||
}, pluck="name")
|
||||
|
||||
if self.document_type not in valid_document_types:
|
||||
frappe.throw(
|
||||
msg=_("Please select valid document type."),
|
||||
title=_("Invalid Document Type")
|
||||
)
|
||||
|
||||
def validate_status_field(self):
|
||||
meta = frappe.get_meta(self.document_type)
|
||||
if not meta.get_field("status"):
|
||||
@ -247,9 +265,15 @@ def get_active_service_level_agreement_for(doc):
|
||||
]
|
||||
|
||||
customer = doc.get('customer')
|
||||
or_filters.append(
|
||||
["Service Level Agreement", "entity", "in", [customer] + get_customer_group(customer) + get_customer_territory(customer)]
|
||||
)
|
||||
if customer:
|
||||
or_filters.extend([
|
||||
["Service Level Agreement", "entity", "in", [customer] + get_customer_group(customer) + get_customer_territory(customer)],
|
||||
["Service Level Agreement", "entity_type", "is", "not set"]
|
||||
])
|
||||
else:
|
||||
or_filters.append(
|
||||
["Service Level Agreement", "entity_type", "is", "not set"]
|
||||
)
|
||||
|
||||
default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]]
|
||||
default_sla = frappe.get_all("Service Level Agreement", filters=default_sla_filter,
|
||||
@ -361,11 +385,18 @@ def apply(doc, method=None):
|
||||
sla = get_active_service_level_agreement_for(doc)
|
||||
|
||||
if not sla:
|
||||
remove_sla_if_applied(doc)
|
||||
return
|
||||
|
||||
process_sla(doc, sla)
|
||||
|
||||
|
||||
def remove_sla_if_applied(doc):
|
||||
doc.service_level_agreement = None
|
||||
doc.response_by = None
|
||||
doc.resolution_by = None
|
||||
|
||||
|
||||
def process_sla(doc, sla):
|
||||
|
||||
if not doc.creation:
|
||||
|
@ -1,13 +0,0 @@
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'service_level_agreement',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Issue'),
|
||||
'items': ['Issue']
|
||||
}
|
||||
]
|
||||
}
|
@ -244,6 +244,13 @@ class TestServiceLevelAgreement(unittest.TestCase):
|
||||
applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement')
|
||||
self.assertEqual(applied_sla, lead_sla.name)
|
||||
|
||||
# check if SLA is removed if condition fails
|
||||
lead.reload()
|
||||
lead.source = None
|
||||
lead.save()
|
||||
applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement')
|
||||
self.assertFalse(applied_sla)
|
||||
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Service Level Agreement"):
|
||||
frappe.delete_doc("Service Level Agreement", d.name, force=1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user