Merge pull request #29278 from shariquerik/update-workspace-json
fix: Updating public workspace json based on new design
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 22c81dd..9e2cdff 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -254,11 +254,13 @@
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 @@
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 @@
'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 @@
'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,
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 8fc4e8c..ac8ab31 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -407,13 +407,14 @@
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 @@
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'):
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index c02c80a..cfa42f6 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1781,47 +1781,6 @@
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 @@
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-.#####'
diff --git a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
index d2c505c..e032bb3 100644
--- a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
+++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
@@ -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
}
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index eab9e12..4775f56 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -185,8 +185,6 @@
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()
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 7073e32..f22669b 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -17,7 +17,7 @@
from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock import get_warehouse_account_map
-from erpnext.stock.stock_ledger import get_items_to_be_repost, get_valuation_rate
+from erpnext.stock.stock_ledger import get_items_to_be_repost
class QualityInspectionRequiredError(frappe.ValidationError): pass
@@ -111,17 +111,6 @@
self.check_expense_account(item_row)
- # If the item does not have the allow zero valuation rate flag set
- # and ( valuation rate not mentioned in an incoming entry
- # or incoming entry not found while delivering the item),
- # try to pick valuation rate from previous sle or Item master and update in SLE
- # Otherwise, throw an exception
-
- if not sle.stock_value_difference and self.doctype != "Stock Reconciliation" \
- and not item_row.get("allow_zero_valuation_rate"):
-
- sle = self.update_stock_ledger_entries(sle)
-
# expense account/ target_warehouse / source_warehouse
if item_row.get('target_warehouse'):
warehouse = item_row.get('target_warehouse')
@@ -164,26 +153,6 @@
return frappe.flags.debit_field_precision
- def update_stock_ledger_entries(self, sle):
- sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
- self.doctype, self.name, currency=self.company_currency, company=self.company)
-
- sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
- sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)
-
- if sle.name:
- frappe.db.sql("""
- update
- `tabStock Ledger Entry`
- set
- stock_value = %(stock_value)s,
- valuation_rate = %(valuation_rate)s,
- stock_value_difference = %(stock_value_difference)s
- where
- name = %(name)s""", (sle))
-
- return sle
-
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
if self.doctype == "Stock Reconciliation":
reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose")
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 5ffbb03..6433a99 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -131,16 +131,14 @@
erpnext.work_order.set_custom_buttons(frm);
frm.set_intro("");
- if (frm.doc.docstatus === 0 && !frm.doc.__islocal) {
+ if (frm.doc.docstatus === 0 && !frm.is_new()) {
frm.set_intro(__("Submit this Work Order for further processing."));
+ } else {
+ frm.trigger("show_progress_for_items");
+ frm.trigger("show_progress_for_operations");
}
if (frm.doc.status != "Closed") {
- if (frm.doc.docstatus===1) {
- frm.trigger('show_progress_for_items');
- frm.trigger('show_progress_for_operations');
- }
-
if (frm.doc.docstatus === 1
&& frm.doc.operations && frm.doc.operations.length) {
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 5190f9f..d56aa6d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -279,7 +279,7 @@
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,7 +313,7 @@
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.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
@@ -325,3 +325,4 @@
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
diff --git a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py
index 8a9633d..4ec22e9 100644
--- a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py
+++ b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py
@@ -9,13 +9,15 @@
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,
)
diff --git a/erpnext/patches/v14_0/delete_healthcare_doctypes.py b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
index 28fc01b..3a4f8f5 100644
--- a/erpnext/patches/v14_0/delete_healthcare_doctypes.py
+++ b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
@@ -47,3 +47,18 @@
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)
diff --git a/erpnext/patches/v14_0/rearrange_company_fields.py b/erpnext/patches/v14_0/rearrange_company_fields.py
new file mode 100644
index 0000000..dd953ff
--- /dev/null
+++ b/erpnext/patches/v14_0/rearrange_company_fields.py
@@ -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)
\ No newline at end of file
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index c0dcb70..4b99421 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -567,16 +567,16 @@
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',
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index ef2bdb6..4b98978 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -53,7 +53,8 @@
{ "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"
}
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 11b684d..e50ff18 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -40,7 +40,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
"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 @@
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 @@
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 @@
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
\ No newline at end of file
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index 2e31c03..15d524d 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -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 @@
- 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')
]
}
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 45e8dcc..dd185fc 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -213,6 +213,9 @@
["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"}],
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 5593101..96751d6 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -292,6 +292,7 @@
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 @@
).run()
flt_reserved_batch_qty = flt(reserved_batch_qty[0][0])
- return flt_reserved_batch_qty
\ No newline at end of file
+ return flt_reserved_batch_qty
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 2947faf..ee55af3 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -402,10 +402,16 @@
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 = []
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index 99000d1..9cdc0f7 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -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 @@
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"
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 8f5d442..306f2c3 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -851,6 +851,34 @@
self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1)
self.assertEqual(se.get("items")[0].amount, 0)
+ def test_zero_incoming_rate(self):
+ """ Make sure incoming rate of 0 is allowed while consuming.
+
+ qty | rate | valuation rate
+ 1 | 100 | 100
+ 1 | 0 | 50
+ -1 | 100 | 0
+ -1 | 0 <--- assert this
+ """
+ item_code = "_TestZeroVal"
+ warehouse = "_Test Warehouse - _TC"
+ create_item('_TestZeroVal')
+ _receipt = make_stock_entry(item_code=item_code, qty=1, to_warehouse=warehouse, rate=100)
+ receipt2 = make_stock_entry(item_code=item_code, qty=1, to_warehouse=warehouse, rate=0, do_not_save=True)
+ receipt2.items[0].allow_zero_valuation_rate = 1
+ receipt2.save()
+ receipt2.submit()
+
+ issue = make_stock_entry(item_code=item_code, qty=1, from_warehouse=warehouse)
+
+ value_diff = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": issue.name, "voucher_type": "Stock Entry"}, "stock_value_difference")
+ self.assertEqual(value_diff, -100)
+
+ issue2 = make_stock_entry(item_code=item_code, qty=1, from_warehouse=warehouse)
+ value_diff = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": issue2.name, "voucher_type": "Stock Entry"}, "stock_value_difference")
+ self.assertEqual(value_diff, 0)
+
+
def test_gle_for_opening_stock_entry(self):
mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1",
company="_Test Company with perpetual inventory", qty=50, basic_rate=100,
diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
index 44e1386..87097c7 100644
--- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
+++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
@@ -55,7 +55,8 @@
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):
diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
index 5f6184d..058af77 100644
--- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
+++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
@@ -91,7 +91,7 @@
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
diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
index 3f49065..cfa1e47 100644
--- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
+++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
@@ -76,6 +76,7 @@
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)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 107bb23..262aa81 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -105,6 +105,7 @@
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 @@
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 @@
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 @@
# 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 @@
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 @@
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 @@
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))
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
index bfbffe2..4dbb0e7 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
@@ -111,6 +111,7 @@
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"]]
]
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index ea617fd..de8f506 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -29,6 +29,7 @@
class ServiceLevelAgreement(Document):
def validate(self):
+ self.validate_selected_doctype()
self.validate_doc()
self.validate_status_field()
self.check_priorities()
@@ -106,6 +107,23 @@
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 @@
]
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 @@
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:
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py
deleted file mode 100644
index 22e2c37..0000000
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from frappe import _
-
-
-def get_data():
- return {
- 'fieldname': 'service_level_agreement',
- 'transactions': [
- {
- 'label': _('Issue'),
- 'items': ['Issue']
- }
- ]
- }
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index b07c862..a34124f 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -244,6 +244,13 @@
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)