Merge branch 'develop' into ledger-merger
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index a6e16a0..8f93811 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -25,20 +25,6 @@
required: true
- type: dropdown
- id: version
- attributes:
- label: Version
- description: Affected versions.
- multiple: true
- options:
- - v12
- - v13
- - v14
- - develop
- validations:
- required: true
-
- - type: dropdown
id: module
attributes:
label: Module
@@ -86,7 +72,7 @@
- manual install
- FrappeCloud
validations:
- required: true
+ required: false
- type: textarea
id: logs
@@ -95,12 +81,7 @@
description: Please copy and paste any relevant log output. This will be automatically formatted.
render: shell
-
- - type: checkboxes
- id: terms
+ - type: markdown
attributes:
- label: Code of Conduct
- description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)
- options:
- - label: I agree to follow this project's Code of Conduct
- required: true
+ value: |
+ By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..15545c0
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1 @@
+hypothesis~=6.31.0
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index a5de50f..0b4696c 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -55,9 +55,9 @@
company.enable_perpetual_inventory = enable
company.save()
-def encode_company_abbr(name, company):
+def encode_company_abbr(name, company=None, abbr=None):
'''Returns name encoded with company abbreviation'''
- company_abbr = frappe.get_cached_value('Company', company, "abbr")
+ company_abbr = abbr or frappe.get_cached_value('Company', company, "abbr")
parts = name.rsplit(" - ", 1)
if parts[-1].lower() != company_abbr.lower():
diff --git a/erpnext/accounts/doctype/account/tests/test_account.js b/erpnext/accounts/doctype/account/tests/test_account.js
deleted file mode 100644
index 039e33e..0000000
--- a/erpnext/accounts/doctype/account/tests/test_account.js
+++ /dev/null
@@ -1,29 +0,0 @@
-QUnit.module('accounts');
-
-QUnit.test("test account", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('Tree', 'Account'),
- () => frappe.timeout(3),
- () => frappe.click_button('Expand All'),
- () => frappe.timeout(1),
- () => frappe.click_link('Debtors'),
- () => frappe.click_button('Edit'),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.doc.root_type=='Asset');
- assert.ok(cur_frm.doc.report_type=='Balance Sheet');
- assert.ok(cur_frm.doc.account_type=='Receivable');
- },
- () => frappe.click_button('Ledger'),
- () => frappe.timeout(1),
- () => {
- // check if general ledger report shown
- assert.deepEqual(frappe.get_route(), ['query-report', 'General Ledger']);
- window.history.back();
- return frappe.timeout(1);
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/account/tests/test_account_with_number.js b/erpnext/accounts/doctype/account/tests/test_account_with_number.js
deleted file mode 100644
index c03e278..0000000
--- a/erpnext/accounts/doctype/account/tests/test_account_with_number.js
+++ /dev/null
@@ -1,69 +0,0 @@
-QUnit.module('accounts');
-
-QUnit.test("test account with number", function(assert) {
- assert.expect(7);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('Tree', 'Account'),
- () => frappe.click_link('Income'),
- () => frappe.click_button('Add Child'),
- () => frappe.timeout(.5),
- () => {
- cur_dialog.fields_dict.account_name.$input.val("Test Income");
- cur_dialog.fields_dict.account_number.$input.val("4010");
- },
- () => frappe.click_button('Create New'),
- () => frappe.timeout(1),
- () => {
- assert.ok($('a:contains("4010 - Test Income"):visible').length!=0, "Account created with number");
- },
- () => frappe.click_link('4010 - Test Income'),
- () => frappe.click_button('Edit'),
- () => frappe.timeout(.5),
- () => frappe.click_button('Update Account Number'),
- () => frappe.timeout(.5),
- () => {
- cur_dialog.fields_dict.account_number.$input.val("4020");
- },
- () => frappe.timeout(1),
- () => cur_dialog.primary_action(),
- () => frappe.timeout(1),
- () => cur_frm.refresh_fields(),
- () => frappe.timeout(.5),
- () => {
- var abbr = frappe.get_abbr(frappe.defaults.get_default("Company"));
- var new_account = "4020 - Test Income - " + abbr;
- assert.ok(cur_frm.doc.name==new_account, "Account renamed");
- assert.ok(cur_frm.doc.account_name=="Test Income", "account name remained same");
- assert.ok(cur_frm.doc.account_number=="4020", "Account number updated to 4020");
- },
- () => frappe.timeout(1),
- () => frappe.click_button('Menu'),
- () => frappe.click_link('Rename'),
- () => frappe.timeout(.5),
- () => {
- cur_dialog.fields_dict.new_name.$input.val("4030 - Test Income");
- },
- () => frappe.timeout(.5),
- () => frappe.click_button("Rename"),
- () => frappe.timeout(2),
- () => {
- assert.ok(cur_frm.doc.account_name=="Test Income", "account name remained same");
- assert.ok(cur_frm.doc.account_number=="4030", "Account number updated to 4030");
- },
- () => frappe.timeout(.5),
- () => frappe.click_button('Chart of Accounts'),
- () => frappe.timeout(.5),
- () => frappe.click_button('Menu'),
- () => frappe.click_link('Refresh'),
- () => frappe.click_button('Expand All'),
- () => frappe.click_link('4030 - Test Income'),
- () => frappe.click_button('Delete'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(.5),
- () => {
- assert.ok($('a:contains("4030 - Test Account"):visible').length==0, "Account deleted");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/account/tests/test_make_tax_account.js b/erpnext/accounts/doctype/account/tests/test_make_tax_account.js
deleted file mode 100644
index a0e09a1..0000000
--- a/erpnext/accounts/doctype/account/tests/test_make_tax_account.js
+++ /dev/null
@@ -1,46 +0,0 @@
-QUnit.module('accounts');
-QUnit.test("test account", assert => {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('Tree', 'Account'),
- () => frappe.click_button('Expand All'),
- () => frappe.click_link('Duties and Taxes - '+ frappe.get_abbr(frappe.defaults.get_default("Company"))),
- () => {
- if($('a:contains("CGST"):visible').length == 0){
- return frappe.map_tax.make('CGST', 9);
- }
- },
- () => {
- if($('a:contains("SGST"):visible').length == 0){
- return frappe.map_tax.make('SGST', 9);
- }
- },
- () => {
- if($('a:contains("IGST"):visible').length == 0){
- return frappe.map_tax.make('IGST', 18);
- }
- },
- () => {
- assert.ok($('a:contains("CGST"):visible').length!=0, "CGST Checked");
- assert.ok($('a:contains("SGST"):visible').length!=0, "SGST Checked");
- assert.ok($('a:contains("IGST"):visible').length!=0, "IGST Checked");
- },
- () => done()
- ]);
-});
-
-
-frappe.map_tax = {
- make:function(text,rate){
- return frappe.run_serially([
- () => frappe.click_button('Add Child'),
- () => frappe.timeout(0.2),
- () => cur_dialog.set_value('account_name',text),
- () => cur_dialog.set_value('account_type','Tax'),
- () => cur_dialog.set_value('tax_rate',rate),
- () => cur_dialog.set_value('account_currency','INR'),
- () => frappe.click_button('Create New'),
- ]);
- }
-};
diff --git a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js
deleted file mode 100644
index f9aa166..0000000
--- a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.module('accounts');
-
-QUnit.test("test: Accounts Settings doesn't allow negatives", function (assert) {
- let done = assert.async();
-
- assert.expect(2);
-
- frappe.run_serially([
- () => frappe.set_route('Form', 'Accounts Settings', 'Accounts Settings'),
- () => frappe.timeout(2),
- () => unchecked_if_checked(cur_frm, 'Allow Stale Exchange Rates', frappe.click_check),
- () => cur_frm.set_value('stale_days', 0),
- () => frappe.click_button('Save'),
- () => frappe.timeout(2),
- () => {
- assert.ok(cur_dialog);
- },
- () => frappe.click_button('Close'),
- () => cur_frm.set_value('stale_days', -1),
- () => frappe.click_button('Save'),
- () => frappe.timeout(2),
- () => {
- assert.ok(cur_dialog);
- },
- () => frappe.click_button('Close'),
- () => done()
- ]);
-
-});
-
-const unchecked_if_checked = function(frm, field_name, fn){
- if (frm.doc.allow_stale) {
- return fn(field_name);
- }
-};
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index e7371fb..4211bd0 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -218,6 +218,8 @@
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+ company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account')
+
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
@@ -226,7 +228,7 @@
total_amount += get_paid_amount(frappe._dict({
'payment_document': voucher['payment_doctype'],
'payment_entry': voucher['payment_name'],
- }), transaction.currency)
+ }), transaction.currency, company_account)
if total_amount > transaction.unallocated_amount:
frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
@@ -261,7 +263,7 @@
return matching
def check_matching(bank_account, company, transaction, document_types):
- # combine all types of vocuhers
+ # combine all types of vouchers
subquery = get_queries(bank_account, company, transaction, document_types)
filters = {
"amount": transaction.unallocated_amount,
@@ -343,13 +345,11 @@
def get_je_matching_query(amount_condition, transaction):
# get matching journal entry query
+ # We have mapping at the bank level
+ # So one bank could have both types of bank accounts like asset and liability
+ # So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
company_account = frappe.get_value("Bank Account", transaction.bank_account, "account")
- root_type = frappe.get_value("Account", company_account, "root_type")
-
- if root_type == "Liability":
- cr_or_dr = "debit" if transaction.withdrawal > 0 else "credit"
- else:
- cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
+ cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
return f"""
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index 4620087..44cea31 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -102,7 +102,7 @@
AND
bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
-def get_paid_amount(payment_entry, currency):
+def get_paid_amount(payment_entry, currency, bank_account):
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
paid_amount_field = "paid_amount"
@@ -115,7 +115,7 @@
payment_entry.payment_entry, paid_amount_field)
elif payment_entry.payment_document == "Journal Entry":
- return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit")
+ return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)")
elif payment_entry.payment_document == "Expense Claim":
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.js b/erpnext/accounts/doctype/journal_entry/test_journal_entry.js
deleted file mode 100644
index 28ccd95..0000000
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.js
+++ /dev/null
@@ -1,39 +0,0 @@
-QUnit.module('Journal Entry');
-
-QUnit.test("test journal entry", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Journal Entry', [
- {posting_date:frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
- {accounts: [
- [
- {'account':'Debtors - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {'party_type':'Customer'},
- {'party':'Test Customer 1'},
- {'credit_in_account_currency':1000},
- {'is_advance':'Yes'},
- ],
- [
- {'account':'HDFC - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {'debit_in_account_currency':1000},
- ]
- ]},
- {cheque_no:1234},
- {cheque_date: frappe.datetime.add_days(frappe.datetime.nowdate(), -1)},
- {user_remark: 'Test'},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.total_debit==1000, "total debit correct");
- assert.ok(cur_frm.doc.total_credit==1000, "total credit correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
index 07c72bd..b5aae98 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
@@ -148,7 +148,7 @@
company.company_name = "_Test Opening Invoice Company"
company.abbr = "_TOIC"
company.default_currency = "INR"
- company.country = "India"
+ company.country = "Pakistan"
company.insert()
return company
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
deleted file mode 100644
index 4f27b74..0000000
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
+++ /dev/null
@@ -1,55 +0,0 @@
-QUnit.module('Payment Entry');
-
-QUnit.test("test payment entry", function(assert) {
- assert.expect(6);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 1},
- {'rate': 101},
- ]
- ]}
- ]);
- },
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(1),
- () => frappe.click_button('Make'),
- () => frappe.timeout(1),
- () => frappe.click_link('Payment'),
- () => frappe.timeout(2),
- () => {
- assert.equal(frappe.get_route()[1], 'Payment Entry',
- 'made payment entry');
- assert.equal(cur_frm.doc.party, 'Test Customer 1',
- 'customer set in payment entry');
- assert.equal(cur_frm.doc.paid_amount, 101,
- 'paid amount set in payment entry');
- assert.equal(cur_frm.doc.references[0].allocated_amount, 101,
- 'amount allocated against sales invoice');
- },
- () => frappe.timeout(1),
- () => cur_frm.set_value('paid_amount', 100),
- () => frappe.timeout(1),
- () => {
- frappe.model.set_value("Payment Entry Reference", cur_frm.doc.references[0].name,
- "allocated_amount", 101);
- },
- () => frappe.timeout(1),
- () => frappe.click_button('Write Off Difference Amount'),
- () => frappe.timeout(1),
- () => {
- assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero');
- assert.equal(cur_frm.doc.deductions[0].amount, 1, 'Write off amount = 1');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js
deleted file mode 100644
index e8db2c3..0000000
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js
+++ /dev/null
@@ -1,60 +0,0 @@
-QUnit.module('Payment Entry');
-
-QUnit.test("test payment entry", function(assert) {
- assert.expect(7 );
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Invoice', [
- {supplier: 'Test Supplier'},
- {bill_no: 'in1234'},
- {items: [
- [
- {'qty': 2},
- {'item_code': 'Test Product 1'},
- {'rate':1000},
- ]
- ]},
- {update_stock:1},
- {supplier_address: 'Test1-Billing'},
- {contact_person: 'Contact 3-Test Supplier'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is just a Test'}
- ]);
- },
-
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => frappe.click_button('Make'),
- () => frappe.timeout(2),
- () => frappe.click_link('Payment'),
- () => frappe.timeout(3),
- () => cur_frm.set_value('mode_of_payment','Cash'),
- () => frappe.timeout(3),
- () => {
- assert.equal(frappe.get_route()[1], 'Payment Entry',
- 'made payment entry');
- assert.equal(cur_frm.doc.party, 'Test Supplier',
- 'supplier set in payment entry');
- assert.equal(cur_frm.doc.paid_amount, 2000,
- 'paid amount set in payment entry');
- assert.equal(cur_frm.doc.references[0].allocated_amount, 2000,
- 'amount allocated against purchase invoice');
- assert.equal(cur_frm.doc.references[0].bill_no, 'in1234',
- 'invoice number allocated against purchase invoice');
- assert.equal(cur_frm.get_field('total_allocated_amount').value, 2000,
- 'correct amount allocated in Write Off');
- assert.equal(cur_frm.get_field('unallocated_amount').value, 0,
- 'correct amount unallocated in Write Off');
- },
-
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
deleted file mode 100644
index 34af79f..0000000
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
+++ /dev/null
@@ -1,28 +0,0 @@
-QUnit.module('Accounts');
-
-QUnit.test("test payment entry", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Payment Entry', [
- {payment_type:'Receive'},
- {mode_of_payment:'Cash'},
- {party_type:'Customer'},
- {party:'Test Customer 3'},
- {paid_amount:675},
- {reference_no:123},
- {reference_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.total_allocated_amount==675, "Allocated AmountCorrect");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
deleted file mode 100644
index 8c7f6f4..0000000
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
+++ /dev/null
@@ -1,67 +0,0 @@
-QUnit.module('Payment Entry');
-
-QUnit.test("test payment entry", function(assert) {
- assert.expect(8);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {company: 'For Testing'},
- {currency: 'INR'},
- {selling_price_list: '_Test Price List'},
- {items: [
- [
- {'qty': 1},
- {'item_code': 'Test Product 1'},
- ]
- ]}
- ]);
- },
- () => frappe.timeout(1),
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1.5),
- () => frappe.click_button('Close'),
- () => frappe.timeout(0.5),
- () => frappe.click_button('Make'),
- () => frappe.timeout(1),
- () => frappe.click_link('Payment'),
- () => frappe.timeout(2),
- () => cur_frm.set_value("paid_to", "_Test Cash - FT"),
- () => frappe.timeout(0.5),
- () => {
- assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry');
- assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry');
- assert.equal(cur_frm.doc.paid_from, 'Debtors - FT', 'customer account set in payment entry');
- assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry');
- assert.equal(cur_frm.doc.references[0].allocated_amount, 100,
- 'amount allocated against sales invoice');
- },
- () => cur_frm.set_value('paid_amount', 95),
- () => frappe.timeout(1),
- () => {
- frappe.model.set_value("Payment Entry Reference",
- cur_frm.doc.references[0].name, "allocated_amount", 100);
- },
- () => frappe.timeout(.5),
- () => {
- assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5');
- },
- () => {
- frappe.db.set_value("Company", "For Testing", "write_off_account", "_Test Write Off - FT");
- frappe.timeout(1);
- frappe.db.set_value("Company", "For Testing",
- "exchange_gain_loss_account", "_Test Exchange Gain/Loss - FT");
- },
- () => frappe.timeout(1),
- () => frappe.click_button('Write Off Difference Amount'),
- () => frappe.timeout(2),
- () => {
- assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero');
- assert.equal(cur_frm.doc.deductions[0].amount, 5, 'Write off amount = 5');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js
deleted file mode 100644
index 8279b59..0000000
--- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js
+++ /dev/null
@@ -1,28 +0,0 @@
-QUnit.module('Pricing Rule');
-
-QUnit.test("test pricing rule", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Pricing Rule", [
- {title: 'Test Pricing Rule'},
- {item_code:'Test Product 2'},
- {selling:1},
- {applicable_for:'Customer'},
- {customer:'Test Customer 3'},
- {currency: frappe.defaults.get_default("currency")}
- {min_qty:1},
- {max_qty:20},
- {valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {discount_percentage:10},
- {for_price_list:'Standard Selling'}
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.item_code=='Test Product 2');
- assert.ok(cur_frm.doc.customer=='Test Customer 3');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js
deleted file mode 100644
index 4a29956..0000000
--- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js
+++ /dev/null
@@ -1,58 +0,0 @@
-QUnit.module('Pricing Rule');
-
-QUnit.test("test pricing rule with different currency", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Pricing Rule", [
- {title: 'Test Pricing Rule 2'},
- {apply_on: 'Item Code'},
- {item_code:'Test Product 4'},
- {selling:1},
- {priority: 1},
- {min_qty:1},
- {max_qty:20},
- {valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {margin_type: 'Amount'},
- {margin_rate_or_amount: 20},
- {rate_or_discount: 'Rate'},
- {rate:200},
- {currency:'USD'}
-
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(0.3),
- () => {
- assert.ok(cur_frm.doc.item_code=='Test Product 4');
- },
-
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {currency: 'INR'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': "Test Product 4"}
- ]
- ]}
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(0.3),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].pricing_rule=='Test Pricing Rule 2', "Pricing rule correct");
- // margin not applied because different currency in pricing rule
- assert.ok(cur_frm.doc.items[0].margin_type==null, "Margin correct");
- },
- () => frappe.timeout(0.3),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js
deleted file mode 100644
index 601ff6b..0000000
--- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js
+++ /dev/null
@@ -1,56 +0,0 @@
-QUnit.module('Pricing Rule');
-
-QUnit.test("test pricing rule with same currency", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Pricing Rule", [
- {title: 'Test Pricing Rule 1'},
- {apply_on: 'Item Code'},
- {item_code:'Test Product 4'},
- {selling:1},
- {min_qty:1},
- {max_qty:20},
- {valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {rate_or_discount: 'Rate'},
- {rate:200},
- {currency:'USD'}
-
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(0.3),
- () => {
- assert.ok(cur_frm.doc.item_code=='Test Product 4');
- },
-
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {currency: 'USD'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': "Test Product 4"}
- ]
- ]}
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(0.3),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].pricing_rule=='Test Pricing Rule 1', "Pricing rule correct");
- assert.ok(cur_frm.doc.items[0].price_list_rate==200, "Item rate correct");
- // get_total
- assert.ok(cur_frm.doc.total== 1000, "Total correct");
- },
- () => frappe.timeout(0.3),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js
deleted file mode 100644
index 94b3b9e..0000000
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js
+++ /dev/null
@@ -1,74 +0,0 @@
-QUnit.module('Purchase Invoice');
-
-QUnit.test("test purchase invoice", function(assert) {
- assert.expect(9);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Invoice', [
- {supplier: 'Test Supplier'},
- {bill_no: 'in123'},
- {items: [
- [
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- {'rate':100},
- ]
- ]},
- {update_stock:1},
- {supplier_address: 'Test1-Billing'},
- {contact_person: 'Contact 3-Test Supplier'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
-
- assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
- assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
-
- },
- () => {
- let date = cur_frm.doc.due_date;
- frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
- frappe.timeout(0.5);
- assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
- },
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(0.5),
- () => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]),
- () => {
- let date = cur_frm.doc.due_date;
- frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
- frappe.timeout(0.5);
- assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
- },
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(0.5),
- () => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]),
- () => {
- let date = cur_frm.doc.due_date;
- frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
- frappe.timeout(0.5);
- assert.ok(!cur_dialog, 'Message is not shown');
- },
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index aa2408e..cb18dd3 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -986,7 +986,7 @@
pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
pi.set_posting_time = 1
- pi.posting_date = '2019-03-15'
+ pi.posting_date = '2019-01-10'
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2019-01-10"
pi.items[0].service_end_date = "2019-03-15"
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js
deleted file mode 100644
index 10b05d0..0000000
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js
+++ /dev/null
@@ -1,28 +0,0 @@
-QUnit.module('Sales Taxes and Charges Template');
-
-QUnit.test("test sales taxes and charges template", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Taxes and Charges Template', [
- {title: "TEST In State GST"},
- {taxes:[
- [
- {charge_type:"On Net Total"},
- {account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
- ],
- [
- {charge_type:"On Net Total"},
- {account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
- ]
- ]}
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.title=='TEST In State GST');
- assert.ok(cur_frm.doc.name=='TEST In State GST - FT');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 64712b5..321b453 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1049,6 +1049,8 @@
frappe.flags.is_reverse_depr_entry = False
asset.flags.ignore_validate_update_after_submit = True
schedule.journal_entry = None
+ depreciation_amount = self.get_depreciation_amount_in_je(reverse_journal_entry)
+ asset.finance_books[0].value_after_depreciation += depreciation_amount
asset.save()
def get_posting_date_of_sales_invoice(self):
@@ -1071,6 +1073,12 @@
return False
+ def get_depreciation_amount_in_je(self, journal_entry):
+ if journal_entry.accounts[0].debit_in_account_currency:
+ return journal_entry.accounts[0].debit_in_account_currency
+ else:
+ return journal_entry.accounts[0].credit_in_account_currency
+
@property
def enable_discount_accounting(self):
if not hasattr(self, "_enable_discount_accounting"):
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js
deleted file mode 100644
index 1c052bd..0000000
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js
+++ /dev/null
@@ -1,73 +0,0 @@
-QUnit.module('Sales Invoice');
-
-QUnit.test("test sales Invoice", function(assert) {
- assert.expect(9);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- {update_stock:1},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==590, "Grand Total correct");
-
- assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
- assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
-
- },
- () => {
- let date = cur_frm.doc.due_date;
- frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
- frappe.timeout(0.5);
- assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
- },
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(0.5),
- () => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]),
- () => {
- let date = cur_frm.doc.due_date;
- frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
- frappe.timeout(0.5);
- assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
- },
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(0.5),
- () => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]),
- () => {
- let date = cur_frm.doc.due_date;
- frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
- frappe.timeout(0.5);
- assert.ok(!cur_dialog, 'Message is not shown');
- },
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js
deleted file mode 100644
index 61d78e1..0000000
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js
+++ /dev/null
@@ -1,42 +0,0 @@
-QUnit.module('Sales Invoice');
-
-QUnit.test("test sales Invoice", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- {update_stock:1},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js
deleted file mode 100644
index cf2d0fb..0000000
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.module('Accounts');
-
-QUnit.test("test sales invoice with margin", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {selling_price_list: 'Test-Selling-USD'},
- {currency: 'USD'},
- {items: [
- [
- {'item_code': 'Test Product 4'},
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 1},
- {'margin_type': 'Percentage'},
- {'margin_rate_or_amount': 20}
- ]
- ]}
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.items[0].rate_with_margin == 240, "Margin rate correct");
- assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 240, "Base margin rate correct");
- assert.ok(cur_frm.doc.total == 240, "Amount correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js
deleted file mode 100644
index 45d9a14..0000000
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js
+++ /dev/null
@@ -1,56 +0,0 @@
-QUnit.module('Sales Invoice');
-
-QUnit.test("test sales Invoice with payment", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- {update_stock:1},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(2),
- () => frappe.tests.click_button('Close'),
- () => frappe.tests.click_button('Make'),
- () => frappe.tests.click_link('Payment'),
- () => frappe.timeout(0.2),
- () => { cur_frm.set_value('mode_of_payment','Cash');},
- () => { cur_frm.set_value('paid_to','Cash - '+frappe.get_abbr(frappe.defaults.get_default('Company')));},
- () => {cur_frm.set_value('reference_no','TEST1234');},
- () => {cur_frm.set_value('reference_date',frappe.datetime.add_days(frappe.datetime.nowdate(), 0));},
- () => cur_frm.save(),
- () => {
- // get payment details
- assert.ok(cur_frm.doc.paid_amount==590, "Paid Amount Correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js
deleted file mode 100644
index 0464e45..0000000
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js
+++ /dev/null
@@ -1,51 +0,0 @@
-QUnit.module('Sales Invoice');
-
-QUnit.test("test sales Invoice with payment request", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- {update_stock:1},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(2),
- () => frappe.tests.click_button('Close'),
- () => frappe.tests.click_button('Make'),
- () => frappe.tests.click_link('Payment Request'),
- () => frappe.timeout(0.2),
- () => { cur_frm.set_value('print_format','GST Tax Invoice');},
- () => { cur_frm.set_value('email_to','test@gmail.com');},
- () => cur_frm.save(),
- () => {
- // get payment details
- assert.ok(cur_frm.doc.grand_total==590, "grand total Correct");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
deleted file mode 100644
index af484d7..0000000
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
+++ /dev/null
@@ -1,44 +0,0 @@
-QUnit.module('Sales Invoice');
-
-QUnit.test("test sales Invoice with serialize item", function(assert) {
- assert.expect(5);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Invoice', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'qty': 2},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {update_stock:1},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- // get batch number
- assert.ok(cur_frm.doc.items[0].batch_no=='TEST-BATCH-001', " Batch Details correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==218, "Grad Total correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js
deleted file mode 100644
index 8cd42f6..0000000
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js
+++ /dev/null
@@ -1,28 +0,0 @@
-QUnit.module('Sales Taxes and Charges Template');
-
-QUnit.test("test sales taxes and charges template", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Taxes and Charges Template', [
- {title: "TEST In State GST"},
- {taxes:[
- [
- {charge_type:"On Net Total"},
- {account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
- ],
- [
- {charge_type:"On Net Total"},
- {account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
- ]
- ]}
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.title=='TEST In State GST');
- assert.ok(cur_frm.doc.name=='TEST In State GST - FT');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js
deleted file mode 100644
index 63ea1bf..0000000
--- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js
+++ /dev/null
@@ -1,36 +0,0 @@
-QUnit.module('Shipping Rule');
-
-QUnit.test("test Shipping Rule", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Shipping Rule", [
- {label: "Next Day Shipping"},
- {shipping_rule_type: "Selling"},
- {calculate_based_on: 'Net Total'},
- {conditions:[
- [
- {from_value:1},
- {to_value:200},
- {shipping_amount:100}
- ],
- [
- {from_value:201},
- {to_value:2000},
- {shipping_amount:50}
- ],
- ]},
- {countries:[
- [
- {country:'India'}
- ]
- ]},
- {account:'Accounts Payable - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
- {cost_center:'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]);
- },
- () => {assert.ok(cur_frm.doc.name=='Next Day Shipping');},
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js b/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js
deleted file mode 100644
index f3668b8..0000000
--- a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js
+++ /dev/null
@@ -1,36 +0,0 @@
-QUnit.module('Shipping Rule');
-
-QUnit.test("test Shipping Rule", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Shipping Rule", [
- {label: "Two Day Shipping"},
- {shipping_rule_type: "Buying"},
- {fixed_shipping_amount: 0},
- {conditions:[
- [
- {from_value:1},
- {to_value:200},
- {shipping_amount:100}
- ],
- [
- {from_value:201},
- {to_value:3000},
- {shipping_amount:200}
- ],
- ]},
- {countries:[
- [
- {country:'India'}
- ]
- ]},
- {account:'Accounts Payable - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
- {cost_center:'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]);
- },
- () => {assert.ok(cur_frm.doc.name=='Two Day Shipping');},
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.js b/erpnext/accounts/doctype/subscription/test_subscription.js
deleted file mode 100644
index 2872a21..0000000
--- a/erpnext/accounts/doctype/subscription/test_subscription.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription", function (assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- // insert a new Subscription
- () => {
- return frappe.tests.make("Subscription", [
- {reference_doctype: 'Sales Invoice'},
- {reference_document: 'SINV-00004'},
- {start_date: frappe.datetime.month_start()},
- {end_date: frappe.datetime.month_end()},
- {frequency: 'Weekly'}
- ]);
- },
- () => cur_frm.savesubmit(),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(2),
- () => {
- assert.ok(cur_frm.doc.frequency.includes("Weekly"), "Set frequency Weekly");
- assert.ok(cur_frm.doc.reference_doctype.includes("Sales Invoice"), "Set base doctype Sales Invoice");
- assert.equal(cur_frm.doc.docstatus, 1, "Submitted subscription");
- assert.equal(cur_frm.doc.next_schedule_date,
- frappe.datetime.add_days(frappe.datetime.get_today(), 7), "Set schedule date");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index 01799d5..758e3e9 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -370,7 +370,7 @@
accounts = get_accounts(root_type, filters)
if not accounts:
- return None, None
+ return None, None, None
accounts = update_parent_account_names(accounts)
diff --git a/erpnext/agriculture/doctype/crop/test_crop.js b/erpnext/agriculture/doctype/crop/test_crop.js
deleted file mode 100644
index 4055563..0000000
--- a/erpnext/agriculture/doctype/crop/test_crop.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Crop", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
-
- frappe.run_serially([
- // insert a new Item
- () => frappe.tests.make('Item', [
- // values to be set
- {item_code: 'Basil Seeds'},
- {item_name: 'Basil Seeds'},
- {item_group: 'Seed'}
- ]),
- // insert a new Item
- () => frappe.tests.make('Item', [
- // values to be set
- {item_code: 'Twigs'},
- {item_name: 'Twigs'},
- {item_group: 'By-product'}
- ]),
- // insert a new Item
- () => frappe.tests.make('Item', [
- // values to be set
- {item_code: 'Basil Leaves'},
- {item_name: 'Basil Leaves'},
- {item_group: 'Produce'}
- ]),
- // insert a new Crop
- () => frappe.tests.make('Crop', [
- // values to be set
- {title: 'Basil from seed'},
- {crop_name: 'Basil'},
- {scientific_name: 'Ocimum basilicum'},
- {materials_required: [
- [
- {item_code: 'Basil Seeds'},
- {qty: '25'},
- {uom: 'Nos'},
- {rate: '1'}
- ],
- [
- {item_code: 'Urea'},
- {qty: '5'},
- {uom: 'Kg'},
- {rate: '10'}
- ]
- ]},
- {byproducts: [
- [
- {item_code: 'Twigs'},
- {qty: '25'},
- {uom: 'Nos'},
- {rate: '1'}
- ]
- ]},
- {produce: [
- [
- {item_code: 'Basil Leaves'},
- {qty: '100'},
- {uom: 'Nos'},
- {rate: '1'}
- ]
- ]},
- {agriculture_task: [
- [
- {task_name: "Plough the field"},
- {start_day: 1},
- {end_day: 1},
- {holiday_management: "Ignore holidays"}
- ],
- [
- {task_name: "Plant the seeds"},
- {start_day: 2},
- {end_day: 3},
- {holiday_management: "Ignore holidays"}
- ],
- [
- {task_name: "Water the field"},
- {start_day: 4},
- {end_day: 4},
- {holiday_management: "Ignore holidays"}
- ],
- [
- {task_name: "First harvest"},
- {start_day: 8},
- {end_day: 8},
- {holiday_management: "Ignore holidays"}
- ],
- [
- {task_name: "Add the fertilizer"},
- {start_day: 10},
- {end_day: 12},
- {holiday_management: "Ignore holidays"}
- ],
- [
- {task_name: "Final cut"},
- {start_day: 15},
- {end_day: 15},
- {holiday_management: "Ignore holidays"}
- ]
- ]}
- ]),
- // agriculture task list
- () => {
- assert.equal(cur_frm.doc.name, 'Basil from seed');
- assert.equal(cur_frm.doc.period, 15);
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js
deleted file mode 100644
index 87184da..0000000
--- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Crop Cycle", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Crop Cycle
- () => frappe.tests.make('Crop Cycle', [
- // values to be set
- {title: 'Basil from seed 2017'},
- {detected_disease: [
- [
- {start_date: '2017-11-21'},
- {disease: 'Aphids'}
- ]
- ]},
- {linked_land_unit: [
- [
- {land_unit: 'Basil Farm'}
- ]
- ]},
- {crop: 'Basil from seed'},
- {start_date: '2017-11-11'},
- {cycle_type: 'Less than a year'}
- ]),
- () => assert.equal(cur_frm.doc.name, 'Basil from seed 2017'),
- () => done()
- ]);
-});
diff --git a/erpnext/agriculture/doctype/disease/test_disease.js b/erpnext/agriculture/doctype/disease/test_disease.js
deleted file mode 100644
index 33f60c4..0000000
--- a/erpnext/agriculture/doctype/disease/test_disease.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Disease", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Disease
- () => frappe.tests.make('Disease', [
- // values to be set
- {common_name: 'Aphids'},
- {scientific_name: 'Aphidoidea'},
- {treatment_task: [
- [
- {task_name: "Survey and find the aphid locations"},
- {start_day: 1},
- {end_day: 2},
- {holiday_management: "Ignore holidays"}
- ],
- [
- {task_name: "Apply Pesticides"},
- {start_day: 3},
- {end_day: 3},
- {holiday_management: "Ignore holidays"}
- ]
- ]}
- ]),
- () => {
- assert.equal(cur_frm.doc.treatment_period, 3);
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.js b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.js
deleted file mode 100644
index 5dd7313..0000000
--- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fertilizer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Item
- () => frappe.tests.make('Item', [
- // values to be set
- {item_code: 'Urea'},
- {item_name: 'Urea'},
- {item_group: 'Fertilizer'}
- ]),
- // insert a new Fertilizer
- () => frappe.tests.make('Fertilizer', [
- // values to be set
- {fertilizer_name: 'Urea'},
- {item: 'Urea'}
- ]),
- () => {
- assert.equal(cur_frm.doc.name, 'Urea');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.js b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.js
deleted file mode 100644
index d93f852..0000000
--- a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Soil Texture", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
-
- frappe.run_serially([
- // insert a new Soil Texture
- () => frappe.tests.make('Soil Texture', [
- // values to be set
- {location: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[72.882185,19.076395]}}]}'},
- {collection_datetime: '2017-11-08'},
- {clay_composition: 20},
- {sand_composition: 30}
- ]),
- () => {
- assert.equal(cur_frm.doc.silt_composition, 50);
- assert.equal(cur_frm.doc.soil_type, 'Silt Loam');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.js b/erpnext/agriculture/doctype/water_analysis/test_water_analysis.js
deleted file mode 100644
index bb01cb3..0000000
--- a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Water Analysis", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Water Analysis
- () => frappe.tests.make('Water Analysis', [
- // values to be set
- {location: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[72.882185,19.076395]}}]}'},
- {collection_datetime: '2017-11-08 18:43:57'},
- {laboratory_testing_datetime: '2017-11-10 18:43:57'}
- ]),
- () => {
- assert.equal(cur_frm.doc.result_datetime, '2017-11-10 18:43:57');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js
deleted file mode 100644
index 012b061..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js
+++ /dev/null
@@ -1,80 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order", function(assert) {
- assert.expect(16);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {currency: 'INR'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 100},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ],
- [
- {"item_code": 'Test Product 1'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"qty": 2},
- {"uom": 'Unit'},
- {"rate": 100},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
-
- {tc_name: 'Test Term 1'},
- {terms: 'This is a term.'}
- ]);
- },
-
- () => {
- // Get supplier details
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
- assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 1), "Schedule Date correct");
- assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Contact email correct");
- // Get item details
- assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct");
- assert.ok(cur_frm.doc.items[0].description == 'Test Product 4', "Description correct");
- assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct");
- assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 2), "Schedule Date correct");
-
- assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.items[1].description == 'Test Product 1', "Description correct");
- assert.ok(cur_frm.doc.items[1].qty == 2, "Quantity correct");
- assert.ok(cur_frm.doc.items[1].schedule_date == cur_frm.doc.schedule_date, "Schedule Date correct");
- // Calculate total
- assert.ok(cur_frm.doc.total == 700, "Total correct");
- // Get terms
- assert.ok(cur_frm.doc.terms == 'This is a term.', "Terms correct");
- },
-
- () => cur_frm.print_doc(),
- () => frappe.timeout(2),
- () => {
- assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
- assert.ok($('div > div:nth-child(5) > div > div > table > tbody > tr > td:nth-child(4) > div').text().includes('Test Product 4'), "Print Preview Works");
- },
-
- () => cur_frm.print_doc(),
- () => frappe.timeout(1),
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
-
- () => {
- assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js
deleted file mode 100644
index bc3d767..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js
+++ /dev/null
@@ -1,61 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order with get items", function(assert) {
- assert.expect(4);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {buying_price_list: 'Test-Buying-USD'},
- {currency: 'USD'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]}
- ]);
- },
-
- () => {
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
- },
-
- () => frappe.timeout(0.3),
- () => frappe.click_button('Get items from'),
- () => frappe.timeout(0.3),
-
- () => frappe.click_link('Product Bundle'),
- () => frappe.timeout(0.5),
-
- () => cur_dialog.set_value('product_bundle', 'Computer'),
- () => frappe.click_button('Get Items'),
- () => frappe.timeout(1),
-
- // Check if items are fetched from Product Bundle
- () => {
- assert.ok(cur_frm.doc.items[1].item_name == 'CPU', "Product bundle item 1 correct");
- assert.ok(cur_frm.doc.items[2].item_name == 'Screen', "Product bundle item 2 correct");
- assert.ok(cur_frm.doc.items[3].item_name == 'Keyboard', "Product bundle item 3 correct");
- },
-
- () => cur_frm.doc.items[1].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
- () => cur_frm.doc.items[2].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
- () => cur_frm.doc.items[3].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
-
- () => cur_frm.save(),
- () => frappe.timeout(1),
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
-
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js
deleted file mode 100644
index daf8d6c..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js
+++ /dev/null
@@ -1,74 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order receipt", function(assert) {
- assert.expect(5);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {buying_price_list: 'Test-Buying-USD'},
- {currency: 'USD'},
- {items: [
- [
- {"item_code": 'Test Product 1'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 100},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
- ]);
- },
-
- () => {
-
- // Check supplier and item details
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
- assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.items[0].description == 'Test Product 1', "Description correct");
- assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct");
-
- },
-
- () => frappe.timeout(1),
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
-
- () => frappe.timeout(1.5),
- () => frappe.click_button('Close'),
- () => frappe.timeout(0.3),
-
- // Make Purchase Receipt
- () => frappe.click_button('Make'),
- () => frappe.timeout(0.3),
-
- () => frappe.click_link('Receipt'),
- () => frappe.timeout(2),
-
- () => cur_frm.save(),
-
- // Save and submit Purchase Receipt
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
-
- // View Purchase order in Stock Ledger
- () => frappe.click_button('View'),
- () => frappe.timeout(0.3),
-
- () => frappe.click_link('Stock Ledger'),
- () => frappe.timeout(2),
- () => {
- assert.ok($('div.slick-cell.l2.r2 > a').text().includes('Test Product 1')
- && $('div.slick-cell.l9.r9 > div').text().includes(5), "Stock ledger entry correct");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js
deleted file mode 100644
index 83eb295..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js
+++ /dev/null
@@ -1,47 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order with discount on grand total", function(assert) {
- assert.expect(4);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {buying_price_list: 'Test-Buying-EUR'},
- {currency: 'EUR'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 500 },
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
- {apply_discount_on: 'Grand Total'},
- {additional_discount_percentage: 10}
- ]);
- },
-
- () => frappe.timeout(1),
-
- () => {
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
- assert.ok(cur_frm.doc.items[0].rate == 500, "Rate correct");
- // Calculate total
- assert.ok(cur_frm.doc.total == 2500, "Total correct");
- // Calculate grand total after discount
- assert.ok(cur_frm.doc.grand_total == 2250, "Grand total correct");
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
-
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js
deleted file mode 100644
index a729dd9..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js
+++ /dev/null
@@ -1,44 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order with item wise discount", function(assert) {
- assert.expect(4);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {buying_price_list: 'Test-Buying-EUR'},
- {currency: 'EUR'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
- {"discount_percentage": 20}
- ]
- ]}
- ]);
- },
-
- () => frappe.timeout(1),
-
- () => {
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
- assert.ok(cur_frm.doc.items[0].discount_percentage == 20, "Discount correct");
- // Calculate totals after discount
- assert.ok(cur_frm.doc.total == 2000, "Total correct");
- assert.ok(cur_frm.doc.grand_total == 2000, "Grand total correct");
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
-
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js
deleted file mode 100644
index b605e76..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js
+++ /dev/null
@@ -1,39 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order with multi UOM", function(assert) {
- assert.expect(3);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 100},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]}
- ]);
- },
-
- () => {
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
- assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct");
- assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi UOM correct");
- },
-
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
-
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js
deleted file mode 100644
index c258756..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js
+++ /dev/null
@@ -1,43 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order with shipping rule", function(assert) {
- assert.expect(3);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {buying_price_list: 'Test-Buying-USD'},
- {currency: 'USD'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 500 },
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
-
- {shipping_rule:'Two Day Shipping'}
- ]);
- },
-
- () => {
- // Check grand total
- assert.ok(cur_frm.doc.total_taxes_and_charges == 200, "Taxes and charges correct");
- assert.ok(cur_frm.doc.grand_total == 2700, "Grand total correct");
- },
-
- () => frappe.timeout(0.3),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
deleted file mode 100644
index ccc383f..0000000
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
+++ /dev/null
@@ -1,44 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: purchase order with taxes and charges", function(assert) {
- assert.expect(3);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {is_subcontracted: 'No'},
- {buying_price_list: 'Test-Buying-USD'},
- {currency: 'USD'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 500 },
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
- {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
-
- {taxes_and_charges: 'TEST In State GST - FT'}
- ]);
- },
-
- () => {
- // Check taxes and calculate grand total
- assert.ok(cur_frm.doc.taxes[1].account_head=='SGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), "Account Head abbr correct");
- assert.ok(cur_frm.doc.total_taxes_and_charges == 225, "Taxes and charges correct");
- assert.ok(cur_frm.doc.grand_total == 2725, "Grand total correct");
- },
-
- () => frappe.timeout(0.3),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
deleted file mode 100644
index 75f85f8..0000000
--- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
+++ /dev/null
@@ -1,76 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: request_for_quotation", function(assert) {
- assert.expect(14);
- let done = assert.async();
- let date;
- frappe.run_serially([
- () => {
- date = frappe.datetime.add_days(frappe.datetime.now_date(), 10);
- return frappe.tests.make('Request for Quotation', [
- {transaction_date: date},
- {suppliers: [
- [
- {"supplier": 'Test Supplier'},
- {"email_id": 'test@supplier.com'}
- ]
- ]},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(),20)},
- {"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
- {message_for_supplier: 'Please supply the specified items at the best possible rates'},
- {tc_name: 'Test Term 1'}
- ]);
- },
- () => frappe.timeout(3),
- () => {
- assert.ok(cur_frm.doc.transaction_date == date, "Date correct");
- assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
- assert.ok(cur_frm.doc.suppliers[0].supplier_name == 'Test Supplier', "Supplier name correct");
- assert.ok(cur_frm.doc.suppliers[0].contact == 'Contact 3-Test Supplier', "Contact correct");
- assert.ok(cur_frm.doc.suppliers[0].email_id == 'test@supplier.com', "Email id correct");
- assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item Name correct");
- assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company")), "Warehouse correct");
- assert.ok(cur_frm.doc.message_for_supplier == 'Please supply the specified items at the best possible rates', "Reply correct");
- assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Term name correct");
- },
- () => frappe.timeout(3),
- () => cur_frm.print_doc(),
- () => frappe.timeout(1),
- () => {
- assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
- assert.ok($('.section-break+ .section-break .column-break:nth-child(1) .value').text().includes("Test Product 4"), "Print Preview Works");
- },
- () => cur_frm.print_doc(),
- () => frappe.timeout(1),
- () => frappe.click_button('Get items from'),
- () => frappe.timeout(0.3),
- () => frappe.click_link('Material Request'),
- () => frappe.timeout(1),
- () => frappe.click_button('Get Items'),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Getting items from material requests work");
- },
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted");
- },
- () => frappe.click_button('Send Supplier Emails'),
- () => frappe.timeout(6),
- () => {
- assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working");
- },
- () => frappe.click_button('Close'),
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js
deleted file mode 100644
index f06c3f3..0000000
--- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js
+++ /dev/null
@@ -1,128 +0,0 @@
-QUnit.module('buying');
-
-QUnit.test("Test: Request for Quotation", function (assert) {
- assert.expect(5);
- let done = assert.async();
- let rfq_name = "";
-
- frappe.run_serially([
- // Go to RFQ list
- () => frappe.set_route("List", "Request for Quotation"),
- // Create a new RFQ
- () => frappe.new_doc("Request for Quotation"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("transaction_date", "04-04-2017"),
- () => cur_frm.set_value("company", "For Testing"),
- // Add Suppliers
- () => {
- cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
- },
- () => frappe.timeout(1),
- () => {
- cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.supplier = "_Test Supplier";
- frappe.click_check('Send Email');
- cur_frm.cur_grid.frm.script_manager.trigger('supplier');
- },
- () => frappe.timeout(1),
- () => {
- cur_frm.cur_grid.toggle_view();
- },
- () => frappe.timeout(1),
- () => frappe.click_button('Add Row',0),
- () => frappe.timeout(1),
- () => {
- cur_frm.fields_dict.suppliers.grid.grid_rows[1].toggle_view();
- },
- () => frappe.timeout(1),
- () => {
- cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.supplier = "_Test Supplier 1";
- frappe.click_check('Send Email');
- cur_frm.cur_grid.frm.script_manager.trigger('supplier');
- },
- () => frappe.timeout(1),
- () => {
- cur_frm.cur_grid.toggle_view();
- },
- () => frappe.timeout(1),
- // Add Item
- () => {
- cur_frm.fields_dict.items.grid.grid_rows[0].toggle_view();
- },
- () => frappe.timeout(1),
- () => {
- cur_frm.fields_dict.items.grid.grid_rows[0].doc.item_code = "_Test Item";
- frappe.set_control('item_code',"_Test Item");
- frappe.set_control('qty',5);
- frappe.set_control('schedule_date', "05-05-2017");
- cur_frm.cur_grid.frm.script_manager.trigger('supplier');
- },
- () => frappe.timeout(2),
- () => {
- cur_frm.cur_grid.toggle_view();
- },
- () => frappe.timeout(2),
- () => {
- cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - FT";
- },
- () => frappe.click_button('Save'),
- () => frappe.timeout(1),
- () => frappe.click_button('Submit'),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(1),
- () => frappe.click_button('Menu'),
- () => frappe.timeout(1),
- () => frappe.click_link('Reload'),
- () => frappe.timeout(1),
- () => {
- assert.equal(cur_frm.doc.docstatus, 1);
- rfq_name = cur_frm.doc.name;
- assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.quote_status == "Pending");
- assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Pending");
- },
- () => {
- cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
- },
- () => frappe.timeout(1),
- () => frappe.timeout(1),
- () => {
- cur_frm.cur_grid.toggle_view();
- },
- () => frappe.click_button('Update'),
- () => frappe.timeout(1),
-
- () => frappe.click_button('Supplier Quotation'),
- () => frappe.timeout(1),
- () => frappe.click_link('Make'),
- () => frappe.timeout(1),
- () => {
- frappe.set_control('supplier',"_Test Supplier 1");
- },
- () => frappe.timeout(1),
- () => frappe.click_button('Make Supplier Quotation'),
- () => frappe.timeout(1),
- () => cur_frm.set_value("company", "For Testing"),
- () => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99,
- () => frappe.timeout(1),
- () => frappe.click_button('Save'),
- () => frappe.timeout(1),
- () => frappe.click_button('Submit'),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(1),
- () => frappe.set_route("List", "Request for Quotation"),
- () => frappe.timeout(2),
- () => frappe.set_route("List", "Request for Quotation"),
- () => frappe.timeout(2),
- () => frappe.click_link(rfq_name),
- () => frappe.timeout(1),
- () => frappe.click_button('Menu'),
- () => frappe.timeout(1),
- () => frappe.click_link('Reload'),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Received");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/supplier/test_supplier.js b/erpnext/buying/doctype/supplier/test_supplier.js
deleted file mode 100644
index eaa4d09..0000000
--- a/erpnext/buying/doctype/supplier/test_supplier.js
+++ /dev/null
@@ -1,77 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: supplier", function(assert) {
- assert.expect(6);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Supplier', [
- {supplier_name: 'Test Supplier'},
- {supplier_group: 'Hardware'},
- {country: 'India'},
- {default_currency: 'INR'},
- {accounts: [
- [
- {'company': "For Testing"},
- {'account': "Creditors - FT"}
- ]]
- }
- ]);
- },
- () => frappe.timeout(1),
- () => frappe.click_button('New Address'),
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {address_title:"Test3"},
- {address_type: "Billing"},
- {address_line1: "Billing Street 3"},
- {city: "Billing City 3"},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => frappe.click_button('New Address'),
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {address_title:"Test3"},
- {address_type: "Shipping"},
- {address_line1: "Shipping Street 3"},
- {city: "Shipping City 3"},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => frappe.click_button('New Address'),
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {address_title:"Test3"},
- {address_type: "Warehouse"},
- {address_line1: "Warehouse Street 3"},
- {city: "Warehouse City 3"},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => frappe.click_button('New Contact'),
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {first_name: "Contact 3"},
- {email_id: "test@supplier.com"}
- ]);
- },
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => frappe.set_route('Form', 'Supplier', 'Test Supplier'),
- () => frappe.timeout(0.3),
-
- () => {
- assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct");
- assert.ok(cur_frm.doc.supplier_group == 'Hardware', "Type correct");
- assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct");
- assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('For Testing'), " Account Head abbr correct");
- assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct");
- assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
deleted file mode 100644
index 20fb430..0000000
--- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
+++ /dev/null
@@ -1,74 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: supplier quotation", function(assert) {
- assert.expect(11);
- let done = assert.async();
- let date;
-
- frappe.run_serially([
- () => {
- date = frappe.datetime.add_days(frappe.datetime.now_date(), 10);
- return frappe.tests.make('Supplier Quotation', [
- {supplier: 'Test Supplier'},
- {transaction_date: date},
- {currency: 'INR'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"rate": 200},
- {"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]
- ]},
- {apply_discount_on: 'Grand Total'},
- {additional_discount_percentage: 10},
- {tc_name: 'Test Term 1'},
- {terms: 'This is a term'}
- ]);
- },
- () => frappe.timeout(3),
- () => {
- // Get Supplier details
- assert.ok(cur_frm.doc.supplier == 'Test Supplier', "Supplier correct");
- assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
- // Get Contact details
- assert.ok(cur_frm.doc.contact_person == 'Contact 3-Test Supplier', "Conatct correct");
- assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Email correct");
- // Get uom
- assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi uom correct");
- assert.ok(cur_frm.doc.total == 1000, "Total correct");
- // Calculate total after discount
- assert.ok(cur_frm.doc.grand_total == 900, "Grand total correct");
- // Get terms
- assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Terms correct");
- },
-
- () => cur_frm.print_doc(),
- () => frappe.timeout(2),
- () => {
- assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
- assert.ok($("table > tbody > tr > td:nth-child(3) > div").text().includes("Test Product 4"), "Print Preview Works As Expected");
- },
- () => cur_frm.print_doc(),
- () => frappe.timeout(1),
- () => frappe.click_button('Get items from'),
- () => frappe.timeout(0.3),
- () => frappe.click_link('Material Request'),
- () => frappe.timeout(0.3),
- () => frappe.click_button('Get Items'),
- () => frappe.timeout(1),
- () => {
- // Get item from Material Requests
- assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Getting items from material requests work");
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
-
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js
deleted file mode 100644
index 0a51565..0000000
--- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js
+++ /dev/null
@@ -1,34 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: supplier quotation with item wise discount", function(assert){
- assert.expect(2);
- let done = assert.async();
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Supplier Quotation', [
- {supplier: 'Test Supplier'},
- {company: 'For Testing'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"uom": 'Unit'},
- {"warehouse": 'All Warehouses - FT'},
- {'discount_percentage': 10},
- ]
- ]}
- ]);
- },
-
- () => {
- assert.ok(cur_frm.doc.total == 900, "Total correct");
- assert.ok(cur_frm.doc.grand_total == 900, "Grand total correct");
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js
deleted file mode 100644
index 7ea3e60..0000000
--- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js
+++ /dev/null
@@ -1,37 +0,0 @@
-QUnit.module('Buying');
-
-QUnit.test("test: supplier quotation with taxes and charges", function(assert) {
- assert.expect(3);
- let done = assert.async();
- let supplier_quotation_name;
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Supplier Quotation', [
- {supplier: 'Test Supplier'},
- {items: [
- [
- {"item_code": 'Test Product 4'},
- {"qty": 5},
- {"rate": 100},
- {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- ]
- ]},
- {taxes_and_charges:'TEST In State GST - FT'},
- ]);
- },
- () => {supplier_quotation_name = cur_frm.doc.name;},
- () => {
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- assert.ok(cur_frm.doc.total_taxes_and_charges == 45, "Taxes and charges correct");
- assert.ok(cur_frm.doc.grand_total == 545, "Grand total correct");
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(0.3),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 2c92820..c862774 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -184,6 +184,8 @@
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/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index 9adbe8b..c31b068 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -8,7 +8,6 @@
from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import (
- cint,
comma_and,
cstr,
get_link_to_form,
@@ -39,11 +38,7 @@
self.check_email_id_is_unique()
self.validate_email_id()
self.validate_contact_date()
- self._prev = frappe._dict({
- "contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None,
- "ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None,
- "contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None,
- })
+ self.set_prev()
def set_full_name(self):
if self.first_name:
@@ -75,6 +70,16 @@
self.add_calendar_event()
self.update_prospects()
+ def set_prev(self):
+ if self.is_new():
+ self._prev = frappe._dict({
+ "contact_date": None,
+ "ends_on": None,
+ "contact_by": None
+ })
+ else:
+ self._prev = frappe.db.get_value("Lead", self.name, ["contact_date", "ends_on", "contact_by"], as_dict=1)
+
def before_insert(self):
self.contact_doc = self.create_contact()
diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py
index 56bfc8f..3882974 100644
--- a/erpnext/crm/doctype/lead/test_lead.py
+++ b/erpnext/crm/doctype/lead/test_lead.py
@@ -23,6 +23,17 @@
customer.customer_group = "_Test Customer Group"
customer.insert()
+ #check whether lead contact is carried forward to the customer.
+ contact = frappe.db.get_value('Dynamic Link', {
+ "parenttype": "Contact",
+ "link_doctype": "Lead",
+ "link_name": customer.lead_name,
+ }, "parent")
+
+ if contact:
+ contact_doc = frappe.get_doc("Contact", contact)
+ self.assertEqual(contact_doc.has_link(customer.doctype, customer.name), True)
+
def test_make_customer_from_organization(self):
from erpnext.crm.doctype.lead.lead import make_customer
diff --git a/erpnext/crm/doctype/lead/tests/test_lead_individual.js b/erpnext/crm/doctype/lead/tests/test_lead_individual.js
deleted file mode 100644
index 66d3337..0000000
--- a/erpnext/crm/doctype/lead/tests/test_lead_individual.js
+++ /dev/null
@@ -1,43 +0,0 @@
-QUnit.module("sales");
-
-QUnit.test("test: lead", function (assert) {
- assert.expect(4);
- let done = assert.async();
- let lead_name = frappe.utils.get_random(10);
- frappe.run_serially([
- // test lead creation
- () => frappe.set_route("List", "Lead"),
- () => frappe.new_doc("Lead"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("lead_name", lead_name),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.doc.lead_name.includes(lead_name),
- 'name correctly set');
- frappe.lead_name = cur_frm.doc.name;
- },
- // create address and contact
- () => frappe.click_link('Address & Contact'),
- () => frappe.click_button('New Address'),
- () => frappe.timeout(1),
- () => frappe.set_control('address_line1', 'Gateway'),
- () => frappe.set_control('city', 'Mumbai'),
- () => cur_frm.save(),
- () => frappe.timeout(3),
- () => assert.equal(frappe.get_route()[1], 'Lead',
- 'back to lead form'),
- () => frappe.click_link('Address & Contact'),
- () => assert.ok($('.address-box').text().includes('Mumbai'),
- 'city is seen in address box'),
-
- // make opportunity
- () => frappe.click_button('Make'),
- () => frappe.click_link('Opportunity'),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.lead, frappe.lead_name,
- 'lead name correctly mapped'),
-
- () => done()
- ]);
-});
diff --git a/erpnext/crm/doctype/lead/tests/test_lead_organization.js b/erpnext/crm/doctype/lead/tests/test_lead_organization.js
deleted file mode 100644
index 7fb9573..0000000
--- a/erpnext/crm/doctype/lead/tests/test_lead_organization.js
+++ /dev/null
@@ -1,55 +0,0 @@
-QUnit.module("sales");
-
-QUnit.test("test: lead", function (assert) {
- assert.expect(5);
- let done = assert.async();
- let lead_name = frappe.utils.get_random(10);
- frappe.run_serially([
- // test lead creation
- () => frappe.set_route("List", "Lead"),
- () => frappe.new_doc("Lead"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("company_name", lead_name),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.doc.lead_name.includes(lead_name),
- 'name correctly set');
- frappe.lead_name = cur_frm.doc.name;
- },
- // create address and contact
- () => frappe.click_link('Address & Contact'),
- () => frappe.click_button('New Address'),
- () => frappe.timeout(1),
- () => frappe.set_control('address_line1', 'Gateway'),
- () => frappe.set_control('city', 'Mumbai'),
- () => cur_frm.save(),
- () => frappe.timeout(3),
- () => assert.equal(frappe.get_route()[1], 'Lead',
- 'back to lead form'),
- () => frappe.click_link('Address & Contact'),
- () => assert.ok($('.address-box').text().includes('Mumbai'),
- 'city is seen in address box'),
-
- () => frappe.click_button('New Contact'),
- () => frappe.timeout(1),
- () => frappe.set_control('first_name', 'John'),
- () => frappe.set_control('last_name', 'Doe'),
- () => cur_frm.save(),
- () => frappe.timeout(3),
- () => frappe.set_route('Form', 'Lead', cur_frm.doc.links[0].link_name),
- () => frappe.timeout(1),
- () => frappe.click_link('Address & Contact'),
- () => assert.ok($('.address-box').text().includes('John'),
- 'contact is seen in contact box'),
-
- // make customer
- () => frappe.click_button('Make'),
- () => frappe.click_link('Customer'),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.lead_name, frappe.lead_name,
- 'lead name correctly mapped'),
-
- () => done()
- ]);
-});
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.js b/erpnext/crm/doctype/opportunity/test_opportunity.js
deleted file mode 100644
index 45b97dd..0000000
--- a/erpnext/crm/doctype/opportunity/test_opportunity.js
+++ /dev/null
@@ -1,56 +0,0 @@
-QUnit.test("test: opportunity", function (assert) {
- assert.expect(8);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('List', 'Opportunity'),
- () => frappe.timeout(1),
- () => frappe.click_button('New'),
- () => frappe.timeout(1),
- () => cur_frm.set_value('opportunity_from', 'Customer'),
- () => cur_frm.set_value('customer', 'Test Customer 1'),
-
- // check items
- () => cur_frm.set_value('with_items', 1),
- () => frappe.tests.set_grid_values(cur_frm, 'items', [
- [
- {item_code:'Test Product 1'},
- {qty: 4}
- ]
- ]),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => {
- assert.notOk(cur_frm.is_new(), 'saved');
- frappe.opportunity_name = cur_frm.doc.name;
- },
-
- // close and re-open
- () => frappe.click_button('Close'),
- () => frappe.timeout(1),
- () => assert.equal(cur_frm.doc.status, 'Closed',
- 'closed'),
-
- () => frappe.click_button('Reopen'),
- () => assert.equal(cur_frm.doc.status, 'Open',
- 'reopened'),
- () => frappe.timeout(1),
-
- // make quotation
- () => frappe.click_button('Make'),
- () => frappe.click_link('Quotation', 1),
- () => frappe.timeout(2),
- () => {
- assert.equal(frappe.get_route()[1], 'Quotation',
- 'made quotation');
- assert.equal(cur_frm.doc.customer, 'Test Customer 1',
- 'customer set in quotation');
- assert.equal(cur_frm.doc.items[0].item_code, 'Test Product 1',
- 'item set in quotation');
- assert.equal(cur_frm.doc.items[0].qty, 4,
- 'qty set in quotation');
- assert.equal(cur_frm.doc.items[0].prevdoc_docname, frappe.opportunity_name,
- 'opportunity set in quotation');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/academic_term/test_academic_term.js b/erpnext/education/doctype/academic_term/test_academic_term.js
deleted file mode 100644
index 383b65a..0000000
--- a/erpnext/education/doctype/academic_term/test_academic_term.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Academic Term', function(assert){
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Academic Term', [
- {academic_year: '2016-17'},
- {term_name: "Semester 1"},
- {term_start_date: '2016-07-20'},
- {term_end_date:'2017-06-20'},
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.academic_year=='2016-17');
- assert.ok(cur_frm.doc.term_name=='Semester 1');
- assert.ok(cur_frm.doc.term_start_date=='2016-07-20');
- assert.ok(cur_frm.doc.term_end_date=='2017-06-20');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js
deleted file mode 100644
index 724c4da..0000000
--- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Education Assessment module
-QUnit.module('education');
-
-QUnit.test('Test: Assessment Criteria', function(assert){
- assert.expect(0);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Assessment Criteria', [
- {assessment_criteria: 'Pass'},
- {assessment_criteria_group: 'Reservation'}
- ]);
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js
deleted file mode 100644
index ab27e63..0000000
--- a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Education Assessment module
-QUnit.module('education');
-
-QUnit.test('Test: Assessment Criteria Group', function(assert){
- assert.expect(0);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Assessment Criteria Group', [
- {assessment_criteria_group: 'Reservation'}
- ]);
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.js b/erpnext/education/doctype/assessment_group/test_assessment_group.js
deleted file mode 100644
index 00e6309..0000000
--- a/erpnext/education/doctype/assessment_group/test_assessment_group.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// Education Assessment module
-QUnit.module('education');
-
-QUnit.test('Test: Assessment Group', function(assert){
- assert.expect(4);
- let done = assert.async();
-
- frappe.run_serially([
- () => frappe.set_route('Tree', 'Assessment Group'),
-
- // Checking adding child without selecting any Node
- () => frappe.tests.click_button('New'),
- () => frappe.timeout(0.2),
- () => {assert.equal($(`.msgprint`).text(), "Select a group node first.", "Error message success");},
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(0.2),
-
- // Creating child nodes
- () => frappe.tests.click_link('All Assessment Groups'),
- () => frappe.map_group.make('Assessment-group-1'),
- () => frappe.map_group.make('Assessment-group-4', "All Assessment Groups", 1),
- () => frappe.tests.click_link('Assessment-group-4'),
- () => frappe.map_group.make('Assessment-group-5', "Assessment-group-3", 0),
-
- // Checking Edit button
- () => frappe.timeout(0.5),
- () => frappe.tests.click_link('Assessment-group-1'),
- () => frappe.tests.click_button('Edit'),
- () => frappe.timeout(0.5),
- () => {assert.deepEqual(frappe.get_route(), ["Form", "Assessment Group", "Assessment-group-1"], "Edit route checks");},
-
- // Deleting child Node
- () => frappe.set_route('Tree', 'Assessment Group'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_link('Assessment-group-1'),
- () => frappe.tests.click_button('Delete'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
-
- // Checking Collapse and Expand button
- () => frappe.timeout(2),
- () => frappe.tests.click_link('Assessment-group-4'),
- () => frappe.click_button('Collapse'),
- () => frappe.tests.click_link('All Assessment Groups'),
- () => frappe.click_button('Collapse'),
- () => {assert.ok($('.opened').size() == 0, 'Collapsed');},
- () => frappe.click_button('Expand'),
- () => {assert.ok($('.opened').size() > 0, 'Expanded');},
-
- () => done()
- ]);
-});
-
-frappe.map_group = {
- make:function(assessment_group_name, parent_assessment_group = 'All Assessment Groups', is_group = 0){
- return frappe.run_serially([
- () => frappe.click_button('Add Child'),
- () => frappe.timeout(0.2),
- () => cur_dialog.set_value('is_group', is_group),
- () => cur_dialog.set_value('assessment_group_name', assessment_group_name),
- () => cur_dialog.set_value('parent_assessment_group', parent_assessment_group),
- () => frappe.click_button('Create New'),
- ]);
- }
-};
diff --git a/erpnext/education/doctype/assessment_plan/test_assessment_plan.js b/erpnext/education/doctype/assessment_plan/test_assessment_plan.js
deleted file mode 100644
index b0bff26..0000000
--- a/erpnext/education/doctype/assessment_plan/test_assessment_plan.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// Testing Assessment Module in education
-QUnit.module('education');
-
-QUnit.test('Test: Assessment Plan', function(assert){
- assert.expect(6);
- let done = assert.async();
- let room_name, instructor_name, assessment_name;
-
- frappe.run_serially([
- () => frappe.db.get_value('Room', {'room_name': 'Room 1'}, 'name'),
- (room) => {room_name = room.message.name;}, // Fetching Room name
- () => frappe.db.get_value('Instructor', {'instructor_name': 'Instructor 1'}, 'name'),
- (instructor) => {instructor_name = instructor.message.name;}, // Fetching Instructor name
-
- () => {
- return frappe.tests.make('Assessment Plan', [
- {assessment_name: "Test-Mid-Term"},
- {assessment_group: 'Assessment-group-5'},
- {maximum_assessment_score: 100},
- {student_group: 'test-course-wise-group-2'},
- {course: 'Test_Sub'},
- {grading_scale: 'GTU'},
- {schedule_date: frappe.datetime.nowdate()},
- {room: room_name},
- {examiner: instructor_name},
- {supervisor: instructor_name},
- {from_time: "12:30:00"},
- {to_time: "2:30:00"}
- ]);
- },
-
- () => {
- assessment_name = cur_frm.doc.name; // Storing the name of current Assessment Plan
- assert.equal(cur_frm.doc.assessment_criteria[0].assessment_criteria, 'Pass', 'Assessment Criteria auto-filled correctly');
- assert.equal(cur_frm.doc.assessment_criteria[0].maximum_score, 100, 'Maximum score correctly set');
- }, // Checking if the table was auto-filled upon selecting appropriate fields
-
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Submit'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5),
- () => {assert.equal(cur_frm.doc.docstatus, 1, "Assessment Plan submitted successfully");},
-
- () => frappe.click_button('Assessment Result'), // Checking out Assessment Result button option
- () => frappe.timeout(0.5),
- () => {
- assert.deepEqual(frappe.get_route(), ["Form", "Assessment Result Tool"], 'Assessment Result properly linked');
- assert.equal(cur_frm.doc.assessment_plan, assessment_name, 'Assessment correctly set');
- assert.equal(cur_frm.doc.student_group, 'test-course-wise-group-2', 'Course for Assessment correctly set');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.js b/erpnext/education/doctype/assessment_result/test_assessment_result.js
deleted file mode 100644
index d4eb4b8..0000000
--- a/erpnext/education/doctype/assessment_result/test_assessment_result.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// Education Assessment module
-QUnit.module('education');
-
-QUnit.test('Test: Assessment Result', function(assert){
- assert.expect(25);
- let done = assert.async();
- let student_list = [];
- let assessment_name;
- let tasks = []
-
- frappe.run_serially([
- // Saving Assessment Plan name
- () => frappe.db.get_value('Assessment Plan', {'assessment_name': 'Test-Mid-Term'}, 'name'),
- (assessment_plan) => {assessment_name = assessment_plan.message.name;},
- // Fetching list of Student for which Result is supposed to be set
- () => frappe.set_route('Form', 'Assessment Plan', assessment_name),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Assessment Result'),
- () => frappe.timeout(1),
- () => cur_frm.refresh(),
- () => frappe.timeout(1),
- () => {
- $("tbody tr").each( function(i, input){
- student_list.push($(input).data().student);
- });
- },
-
- // Looping through each student in the list and setting up their score
- () => {
- student_list.forEach(index => {
- tasks.push(
- () => frappe.set_route('List', 'Assessment Result', 'List'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('New'),
- () => frappe.timeout(0.5),
- () => cur_frm.set_value('student', index),
- () => cur_frm.set_value('assessment_plan', assessment_name),
- () => frappe.timeout(0.2),
- () => cur_frm.doc.details[0].score = (39 + (15 * student_list.indexOf(index))),
- () => cur_frm.save(),
- () => frappe.timeout(0.5),
-
- () => frappe.db.get_value('Assessment Plan', {'name': 'ASP00001'}, ['grading_scale', 'maximum_assessment_score']),
- (assessment_plan) => {
- assert.equal(cur_frm.doc.grading_scale, assessment_plan.message.grading_scale, 'Grading scale correctly fetched');
- assert.equal(cur_frm.doc.maximum_score, assessment_plan.message.maximum_assessment_score, 'Maximum score correctly fetched');
-
- frappe.call({
- method: "erpnext.education.api.get_grade",
- args: {
- "grading_scale": assessment_plan.message.grading_scale,
- "percentage": cur_frm.doc.total_score
- },
- callback: function(r){
- assert.equal(cur_frm.doc.grade, r.message, "Grade correctly calculated");
- }
- });
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5),
- () => {assert.equal();},
- () => {assert.equal(cur_frm.doc.docstatus, 1, "Submitted successfully");},
- );
- });
- return frappe.run_serially(tasks);
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js
deleted file mode 100644
index 7ef5c68..0000000
--- a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// Education Assessment module
-QUnit.module('education');
-
-QUnit.test('Test: Assessment Result Tool', function(assert){
- assert.expect(1);
- let done = assert.async();
- let i, count = 0, assessment_name;
-
- frappe.run_serially([
- // Saving Assessment Plan name
- () => frappe.db.get_value('Assessment Plan', {'assessment_name': 'Test-Mid-Term'}, 'name'),
- (assessment_plan) => {assessment_name = assessment_plan.message.name;},
-
- () => frappe.set_route('Form', 'Assessment Plan', assessment_name),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Assessment Result'),
- () => frappe.timeout(1),
- () => cur_frm.refresh(),
- () => frappe.timeout(1),
- () => {
- for(i = 2; i < $('tbody tr').size() * 4; i = (i + 4)){
- if(($(`tbody td:eq("${i}")`) != "") && ($(`tbody td:eq("${i+1}")`) != ""))
- count++;
- }
- assert.equal($('tbody tr').size(), count, 'All grades correctly displayed');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/course/test_course.js b/erpnext/education/doctype/course/test_course.js
deleted file mode 100644
index 2b6860c..0000000
--- a/erpnext/education/doctype/course/test_course.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// Testing Setup Module in education
-QUnit.module('education');
-
-QUnit.test('test course', function(assert) {
- assert.expect(8);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Course', [
- {course_name: 'Test_Subject'},
- {course_code: 'Test_Sub'},
- {department: 'Test Department'},
- {course_abbreviation: 'Test_Sub'},
- {course_intro: 'Test Subject Intro'},
- {default_grading_scale: 'GTU'},
- {assessment_criteria: [
- [
- {assessment_criteria: 'Pass'},
- {weightage: 100}
- ]
- ]}
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.course_name == 'Test_Subject', 'Course name correctly set');
- assert.ok(cur_frm.doc.course_code == 'Test_Sub', 'Course code correctly set');
- assert.ok(cur_frm.doc.department == 'Test Department', 'Department selected correctly');
- assert.ok(cur_frm.doc.course_abbreviation == 'Test_Sub');
- assert.ok(cur_frm.doc.course_intro == 'Test Subject Intro');
- assert.ok(cur_frm.doc.default_grading_scale == 'GTU', 'Grading scale selected correctly');
- assert.ok(cur_frm.doc.assessment_criteria[0].assessment_criteria == 'Pass', 'Assessment criteria selected correctly');
- assert.ok(cur_frm.doc.assessment_criteria[0].weightage == '100');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/education_settings/test_education_settings.js b/erpnext/education/doctype/education_settings/test_education_settings.js
deleted file mode 100644
index 990b0aa..0000000
--- a/erpnext/education/doctype/education_settings/test_education_settings.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test("test: Education Settings", function (assert) {
- let done = assert.async();
-
- assert.expect(3);
-
- frappe.run_serially([
- () => frappe.set_route("List", "Education Settings"),
- () => frappe.timeout(0.4),
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {current_academic_year: '2016-17'},
- {current_academic_term: '2016-17 (Semester 1)'},
- {attendance_freeze_date: '2016-07-20'}
- ]);
- },
- () => {
- cur_frm.save();
- assert.ok(cur_frm.doc.current_academic_year=="2016-17");
- assert.ok(cur_frm.doc.current_academic_term=="2016-17 (Semester 1)");
- assert.ok(cur_frm.doc.attendance_freeze_date=="2016-07-20");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/fees/test_fees.js b/erpnext/education/doctype/fees/test_fees.js
deleted file mode 100644
index 22e987e..0000000
--- a/erpnext/education/doctype/fees/test_fees.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fees", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Fees', [
-
- // insert a new Fees
- () => {
- return frappe.tests.make('Fees', [
- {student: 'STUD00001'},
- {due_date: frappe.datetime.get_today()},
- {fee_structure: 'FS00001'}
- ]);
- },
- () => {
- assert.equal(cur_frm.doc.grand_total===cur_frm.doc.outstanding_amount);
- },
- () => frappe.timeout(0.3),
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/grading_scale/test_grading_scale.js b/erpnext/education/doctype/grading_scale/test_grading_scale.js
deleted file mode 100644
index fb56918..0000000
--- a/erpnext/education/doctype/grading_scale/test_grading_scale.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// Education Assessment module
-QUnit.module('education');
-
-QUnit.test('Test: Grading Scale', function(assert){
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Grading Scale', [
- {grading_scale_name: 'GTU'},
- {description: 'The score will be set according to 100 based system.'},
- {intervals: [
- [
- {grade_code: 'AA'},
- {threshold: '95'},
- {grade_description: 'First Class + Distinction'}
- ],
- [
- {grade_code: 'AB'},
- {threshold: '90'},
- {grade_description: 'First Class'}
- ],
- [
- {grade_code: 'BB'},
- {threshold: '80'},
- {grade_description: 'Distinction'}
- ],
- [
- {grade_code: 'BC'},
- {threshold: '70'},
- {grade_description: 'Second Class'}
- ],
- [
- {grade_code: 'CC'},
- {threshold: '60'},
- {grade_description: 'Third Class'}
- ],
- [
- {grade_code: 'CD'},
- {threshold: '50'},
- {grade_description: 'Average'}
- ],
- [
- {grade_code: 'DD'},
- {threshold: '40'},
- {grade_description: 'Pass'}
- ],
- [
- {grade_code: 'FF'},
- {threshold: '0'},
- {grade_description: 'Fail'}
- ],
- ]}
- ]);
- },
- () => {
- return frappe.tests.make('Grading Scale', [
- {grading_scale_name: 'GTU-2'},
- {description: 'The score will be set according to 100 based system.'},
- {intervals: [
- [
- {grade_code: 'AA'},
- {threshold: '90'},
- {grade_description: 'Distinction'}
- ],
- [
- {grade_code: 'FF'},
- {threshold: '0'},
- {grade_description: 'Fail'}
- ]
- ]}
- ]);
- },
-
- () => {
- let grading_scale = ['GTU', 'GTU-2'];
- let tasks = [];
- grading_scale.forEach(index => {
- tasks.push(
- () => frappe.set_route('Form', 'Grading Scale', index),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Submit'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
- () => {assert.equal(cur_frm.doc.docstatus, 1, 'Submitted successfully');}
- );
- });
- return frappe.run_serially(tasks);
- },
-
- () => frappe.timeout(1),
- () => frappe.set_route('Form', 'Grading Scale','GTU-2'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Cancel'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5),
- () => {assert.equal(cur_frm.doc.docstatus, 2, 'Cancelled successfully');},
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/guardian/test_guardian.js b/erpnext/education/doctype/guardian/test_guardian.js
deleted file mode 100644
index 1ea6dc2..0000000
--- a/erpnext/education/doctype/guardian/test_guardian.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Testing Student Module in education
-QUnit.module('education');
-
-QUnit.test('Test: Guardian', function(assert){
- assert.expect(9);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Guardian', [
- {guardian_name: 'Test Guardian'},
- {email_address: 'guardian@testmail.com'},
- {mobile_number: 9898980000},
- {alternate_number: 8989890000},
- {date_of_birth: '1982-07-22'},
- {education: 'Testing'},
- {occupation: 'Testing'},
- {designation: 'Testing'},
- {work_address: 'Testing address'}
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.guardian_name == 'Test Guardian');
- assert.ok(cur_frm.doc.email_address == 'guardian@testmail.com');
- assert.ok(cur_frm.doc.mobile_number == 9898980000);
- assert.ok(cur_frm.doc.alternate_number == 8989890000);
- assert.ok(cur_frm.doc.date_of_birth == '1982-07-22');
- assert.ok(cur_frm.doc.education == 'Testing');
- assert.ok(cur_frm.doc.occupation == 'Testing');
- assert.ok(cur_frm.doc.designation == 'Testing');
- assert.ok(cur_frm.doc.work_address == 'Testing address');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/instructor/test_instructor.js b/erpnext/education/doctype/instructor/test_instructor.js
deleted file mode 100644
index c584f45..0000000
--- a/erpnext/education/doctype/instructor/test_instructor.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// Testing Setup Module in education
-QUnit.module('education');
-
-QUnit.test('Test: Instructor', function(assert){
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Instructor", [
- {instructor_name: 'Instructor 1'},
- {department: 'Test Department'}
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.instructor_name == 'Instructor 1');
- assert.ok(cur_frm.doc.department = 'Test Department');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/program/test_program.js b/erpnext/education/doctype/program/test_program.js
deleted file mode 100644
index b9ca41a..0000000
--- a/erpnext/education/doctype/program/test_program.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Testing Setup Module in education
-QUnit.module('education');
-
-QUnit.test('Test: Program', function(assert){
- assert.expect(6);
- let done = assert.async();
- let fee_structure_code;
- frappe.run_serially([
- () => {
- return frappe.tests.make('Program', [
- {program_name: 'Standard Test'},
- {program_code: 'Standard Test'},
- {department: 'Test Department'},
- {program_abbreviation: 'Standard Test'},
- {courses: [
- [
- {course: 'Test_Sub'},
- {required: true}
- ]
- ]}
- ]);
- },
-
- () => {
- assert.ok(cur_frm.doc.program_name == 'Standard Test');
- assert.ok(cur_frm.doc.program_code == 'Standard Test');
- assert.ok(cur_frm.doc.department == 'Test Department');
- assert.ok(cur_frm.doc.program_abbreviation == 'Standard Test');
- assert.ok(cur_frm.doc.courses[0].course == 'Test_Sub');
- assert.ok(cur_frm.doc.courses[0].required == true);
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/room/test_room.js b/erpnext/education/doctype/room/test_room.js
deleted file mode 100644
index fdcbe92..0000000
--- a/erpnext/education/doctype/room/test_room.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Room', function(assert){
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Room', [
- {room_name: 'Room 1'},
- {room_number: '1'},
- {seating_capacity: '60'},
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.room_name == 'Room 1');
- assert.ok(cur_frm.doc.room_number = '1');
- assert.ok(cur_frm.doc.seating_capacity = '60');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js
deleted file mode 100644
index e01791a..0000000
--- a/erpnext/education/doctype/student_admission/test_student_admission.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Testing Admission Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Admission', function(assert) {
- assert.expect(10);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Student Admission', [
- {academic_year: '2016-17'},
- {admission_start_date: '2016-04-20'},
- {admission_end_date: '2016-05-31'},
- {title: '2016-17 Admissions'},
- {enable_admission_application: 1},
- {introduction: 'Test intro'},
- {program_details: [
- [
- {'program': 'Standard Test'},
- {'application_fee': 1000},
- {'applicant_naming_series': 'AP'},
- ]
- ]}
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.academic_year == '2016-17');
- assert.ok(cur_frm.doc.admission_start_date == '2016-04-20');
- assert.ok(cur_frm.doc.admission_end_date == '2016-05-31');
- assert.ok(cur_frm.doc.title == '2016-17 Admissions');
- assert.ok(cur_frm.doc.enable_admission_application == 1);
- assert.ok(cur_frm.doc.introduction == 'Test intro');
- assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected');
- assert.ok(cur_frm.doc.program_details[0].application_fee == 1000);
- assert.ok(cur_frm.doc.program_details[0].applicant_naming_series == 'AP');
- assert.ok(cur_frm.doc.route == 'admissions/2016-17-Admissions', "Route successfully set");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js
deleted file mode 100644
index fa67977..0000000
--- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js
+++ /dev/null
@@ -1,95 +0,0 @@
-// Testing Admission module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Applicant', function(assert){
- assert.expect(24);
- let done = assert.async();
- let guradian_auto_code;
- let guardian_name;
- frappe.run_serially([
- () => frappe.set_route('List', 'Guardian'),
- () => frappe.timeout(0.5),
- () => {$(`a:contains("Test Guardian"):visible`)[0].click();},
- () => frappe.timeout(1),
- () => {
- guardian_name = cur_frm.doc.guardian_name;
- guradian_auto_code = frappe.get_route()[2];
- },
- // Testing data entry for Student Applicant
- () => {
- return frappe.tests.make('Student Applicant',[
- {first_name: 'Fname'},
- {middle_name: 'Mname'},
- {last_name: 'Lname'},
- {program: 'Standard Test'},
- {student_admission: '2016-17 Admissions'},
- {academic_year: '2016-17'},
- {date_of_birth: '1995-07-20'},
- {student_email_id: 'test@testmail.com'},
- {gender: 'Male'},
- {student_mobile_number: '9898980000'},
- {blood_group: 'O+'},
- {address_line_1: 'Test appt, Test Society,'},
- {address_line_2: 'Test district, Test city.'},
- {city: 'Test'},
- {state: 'Test'},
- {pincode: '400086'}
- ]);
- },
- // Entry in Guardian child table
- () => $('a:contains("Guardian Details"):visible').click(),
- () => $('.btn:contains("Add Row"):visible').click(),
- () => {
- cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian = guradian_auto_code;
- cur_frm.get_field("guardians").grid.grid_rows[0].doc.relation = "Father";
- cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian_name = guardian_name;
- $('a:contains("Guardian Details"):visible').click();
- },
- // Entry in Sibling child table
- () => $('a:contains("Sibling Details"):visible').click(),
- () => $('.btn:contains("Add Row"):visible').click(),
- () => {
- cur_frm.get_field("siblings").grid.grid_rows[0].doc.full_name = "Test Name";
- cur_frm.get_field("siblings").grid.grid_rows[0].doc.gender = "Male";
- cur_frm.get_field("siblings").grid.grid_rows[0].doc.institution = "Test Institution";
- cur_frm.get_field("siblings").grid.grid_rows[0].doc.program = "Test Program";
- cur_frm.get_field("siblings").grid.grid_rows[0].doc.date_of_birth = "1995-07-20";
- $('span.hidden-xs.octicon.octicon-triangle-up').click();
- cur_frm.save();
- },
- () => {
- assert.ok(cur_frm.doc.first_name == 'Fname');
- assert.ok(cur_frm.doc.middle_name == 'Mname');
- assert.ok(cur_frm.doc.last_name == 'Lname');
- assert.ok(cur_frm.doc.program == 'Standard Test', 'Program selected correctly');
- assert.ok(cur_frm.doc.student_admission == '2016-17 Admissions', 'Student Admission entry correctly selected');
- assert.ok(cur_frm.doc.academic_year == '2016-17');
- assert.ok(cur_frm.doc.date_of_birth == '1995-07-20');
- assert.ok(cur_frm.doc.student_email_id == 'test@testmail.com');
- assert.ok(cur_frm.doc.gender == 'Male');
- assert.ok(cur_frm.doc.student_mobile_number == '9898980000');
- assert.ok(cur_frm.doc.blood_group == 'O+');
- assert.ok(cur_frm.doc.address_line_1 == 'Test appt, Test Society,');
- assert.ok(cur_frm.doc.address_line_2 == 'Test district, Test city.');
- assert.ok(cur_frm.doc.city == 'Test');
- assert.ok(cur_frm.doc.state == 'Test');
- assert.ok(cur_frm.doc.pincode == '400086');
- },
- () => frappe.timeout(1),
- () => $('a:contains("Guardian Details"):visible').click(),
- () => {
- assert.ok(cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian == guradian_auto_code, 'Guardian correctly selected from dropdown');
- assert.ok(cur_frm.get_field("guardians").grid.grid_rows[0].doc.relation == 'Father');
- assert.ok(cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian_name == guardian_name, 'Guardian name was correctly retrieved');
- },
- () => $('a:contains("Sibling Details"):visible').click(),
- () => {
- assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.full_name == 'Test Name');
- assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.gender == 'Male');
- assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.institution == 'Test Institution');
- assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.program == 'Test Program');
- assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.date_of_birth == '1995-07-20');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
deleted file mode 100644
index 03101e4..0000000
--- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
+++ /dev/null
@@ -1,87 +0,0 @@
-QUnit.module('Admission');
-
-QUnit.test('Make Students', function(assert){
- assert.expect(0);
- let done = assert.async();
- let tasks = [];
- let loop = [1,2,3,4];
- let fname;
-
- frappe.run_serially([
- // Making School House to be used in this test and later
- () => frappe.set_route('Form', 'School House/New School House'),
- () => frappe.timeout(0.5),
- () => cur_frm.doc.house_name = 'Test_house',
- () => cur_frm.save(),
-
- // Making Student Applicant entries
- () => {
- loop.forEach(index => {
- tasks.push(() => {
- fname = "Fname" + index;
-
- return frappe.tests.make('Student Applicant', [
- {first_name: fname},
- {middle_name: "Mname"},
- {last_name: "Lname"},
- {program: "Standard Test"},
- {student_admission: "2016-17 Admissions"},
- {date_of_birth: '1995-08-20'},
- {student_email_id: ('test' + (index+3) + '@testmail.com')},
- {gender: 'Male'},
- {student_mobile_number: (9898980000 + index)},
- {blood_group: 'O+'},
- {address_line_1: 'Test appt, Test Society,'},
- {address_line_2: 'Test district, Test city.'},
- {city: 'Test'},
- {state: 'Test'},
- {pincode: '395007'}
- ]);
- });
- });
- return frappe.run_serially(tasks);
- },
-
- // Using Program Enrollment Tool to enroll all dummy student at once
- () => frappe.set_route('Form', 'Program Enrollment Tool'),
- () => {
- cur_frm.set_value("get_students_from", "Student Applicants");
- cur_frm.set_value("academic_year", "2016-17");
- cur_frm.set_value("program", "Standard Test");
- },
- () => frappe.tests.click_button("Get Students"),
- () => frappe.timeout(1),
- () => frappe.tests.click_button("Enroll Students"),
- () => frappe.timeout(1.5),
- () => frappe.tests.click_button("Close"),
-
- // Submitting required data for each enrolled Student
- () => {
- tasks = [];
- loop.forEach(index => {
- tasks.push(
- () => {fname = "Fname" + index + " Mname Lname";},
- () => frappe.set_route('List', 'Program Enrollment/List'),
- () => frappe.timeout(0.6),
- () => frappe.tests.click_link(fname),
- () => frappe.timeout(0.4),
- () => {
- cur_frm.set_value('program', 'Standard Test');
- cur_frm.set_value('student_category', 'Reservation');
- cur_frm.set_value('student_batch_name', 'A');
- cur_frm.set_value('academic_year', '2016-17');
- cur_frm.set_value('academic_term', '2016-17 (Semester 1)');
- cur_frm.set_value('school_house', 'Test_house');
- },
- () => cur_frm.save(),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5)
- );
- });
- return frappe.run_serially(tasks);
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js
deleted file mode 100644
index daa36e7..0000000
--- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// Testing Admission module in Education
-QUnit.module('education');
-
-QUnit.test('test student applicant', function(assert){
- assert.expect(11);
- let done = assert.async();
- let testing_status;
- frappe.run_serially([
- () => frappe.set_route('List', 'Student Applicant'),
- () => frappe.timeout(0.5),
- () => {$(`a:contains("Fname Mname Lname"):visible`)[0].click();},
-
- // Checking different options
- // 1. Moving forward with Submit
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5),
- () => {
- testing_status = $('span.indicator.orange').text();
- assert.ok(testing_status.indexOf('Submit this document to confirm') == -1); // checking if submit has been successfull
- },
-
- // 2. Cancelling the Submit request
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Cancel'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5),
- () => {
- testing_status = $('h1.editable-title').text();
- assert.ok(testing_status.indexOf('Cancelled') != -1); // checking if cancel request has been successfull
- },
-
- // 3. Checking Amend option
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Amend'),
- () => cur_frm.doc.student_email_id = "test2@testmail.com", // updating email id since same id again is not allowed
- () => cur_frm.save(),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'), // Submitting again after amend
- () => {
- testing_status = $('span.indicator.orange').text();
- assert.ok(testing_status.indexOf('Submit this document to confirm') == -1); // checking if submit has been successfull after amend
- },
-
- // Checking different Application status option
- () => {
- testing_status = $('h1.editable-title').text();
- assert.ok(testing_status.indexOf('Applied') != -1); // checking if Applied has been successfull
- },
- () => cur_frm.set_value('application_status', "Rejected"), // Rejected Status
- () => frappe.tests.click_button('Update'),
- () => {
- testing_status = $('h1.editable-title').text();
- assert.ok(testing_status.indexOf('Rejected') != -1); // checking if Rejected has been successfull
- },
- () => cur_frm.set_value('application_status', "Admitted"), // Admitted Status
- () => frappe.tests.click_button('Update'),
- () => {
- testing_status = $('h1.editable-title').text();
- assert.ok(testing_status.indexOf('Admitted') != -1); // checking if Admitted has been successfull
- },
- () => cur_frm.set_value('application_status', "Approved"), // Approved Status
- () => frappe.tests.click_button('Update'),
- () => {
- testing_status = $('h1.editable-title').text();
- assert.ok(testing_status.indexOf('Approved') != -1); // checking if Approved has been successfull
- },
-
- // Clicking on Enroll button should add the applicant's entry in Student doctype, and take you to Program Enrollment page
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Enroll'),
- () => frappe.timeout(0.5),
- () => {
- assert.ok(frappe.get_route()[0] == 'Form'); // Checking if the current page is Program Enrollment page or not
- assert.ok(frappe.get_route()[1] == 'Program Enrollment');
- },
-
- // Routing to Student List to check if the Applicant's entry has been made or not
- () => frappe.timeout(0.5),
- () => frappe.set_route('List', 'Student'),
- () => frappe.timeout(0.5),
- () => {$(`a:contains("Fname Mname Lname"):visible`)[0].click();},
- () => frappe.timeout(0.5),
- () => {assert.ok(($(`h1.editable-title`).text()).indexOf('Enabled') != -1, 'Student entry successfully created');}, // Checking if the Student entry has been enabled
- // Enrolling the Student into a Program
- () => {$('.form-documents .row:nth-child(1) .col-xs-6:nth-child(1) .octicon-plus').click();},
- () => frappe.timeout(1),
- () => cur_frm.set_value('program', 'Standard Test'),
- () => frappe.timeout(1),
- () => {
- cur_frm.set_value('student_category', 'Reservation');
- cur_frm.set_value('student_batch_name', 'A');
- cur_frm.set_value('academic_year', '2016-17');
- cur_frm.set_value('academic_term', '2016-17 (Semester 1)');
- cur_frm.set_value('school_house', 'Test_house');
- },
- () => cur_frm.save(),
-
- // Submitting Program Enrollment form for our Test Student
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => {
- assert.ok(cur_frm.doc.docstatus == 1, "Program enrollment successfully submitted");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.js b/erpnext/education/doctype/student_attendance/test_student_attendance.js
deleted file mode 100644
index 3d30b09..0000000
--- a/erpnext/education/doctype/student_attendance/test_student_attendance.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Testing Attendance Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Attendance', function(assert){
- assert.expect(2);
- let done = assert.async();
- let student_code;
-
- frappe.run_serially([
- () => frappe.db.get_value('Student', {'student_email_id': 'test2@testmail.com'}, 'name'),
- (student) => {student_code = student.message.name;}, // fetching student code from db
-
- () => {
- return frappe.tests.make('Student Attendance', [
- {student: student_code},
- {date: frappe.datetime.nowdate()},
- {student_group: "test-batch-wise-group-2"},
- {status: "Absent"}
- ]);
- },
-
- () => frappe.timeout(0.5),
- () => {assert.equal(cur_frm.doc.status, "Absent", "Attendance correctly saved");},
-
- () => frappe.timeout(0.5),
- () => cur_frm.set_value("status", "Present"),
- () => {assert.equal(cur_frm.doc.status, "Present", "Attendance correctly saved");},
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js
deleted file mode 100644
index b66d839..0000000
--- a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js
+++ /dev/null
@@ -1,85 +0,0 @@
-// Testing Attendance Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Attendace Tool', function(assert){
- assert.expect(10);
- let done = assert.async();
- let i, count = 0;
-
- frappe.run_serially([
- () => frappe.timeout(0.2),
- () => frappe.set_route('Form', 'Student Attendance Tool'),
- () => frappe.timeout(0.5),
-
- () => {
- if(cur_frm.doc.based_on == 'Student Group' || cur_frm.doc.based_on == 'Course Schedule'){
- cur_frm.doc.based_on = 'Student Group';
- assert.equal(1, 1, 'Attendance basis correctly set');
- cur_frm.set_value("group_based_on", 'Batch');
- cur_frm.set_value("student_group", "test-batch-wise-group");
- cur_frm.set_value("date", frappe.datetime.nowdate());
- }
- },
- () => frappe.timeout(0.5),
- () => {
- assert.equal($('input.students-check').size(), 5, "Student list based on batch correctly fetched");
- assert.equal(frappe.datetime.nowdate(), cur_frm.doc.date, 'Current date correctly set');
-
- cur_frm.set_value("student_group", "test-batch-wise-group-2");
- assert.equal($('input.students-check').size(), 5, "Student list based on batch 2 correctly fetched");
-
- cur_frm.set_value("group_based_on", 'Course');
-
- cur_frm.set_value("student_group", "test-course-wise-group");
- assert.equal($('input.students-check').size(), 5, "Student list based on course correctly fetched");
-
- cur_frm.set_value("student_group", "test-course-wise-group-2");
- assert.equal($('input.students-check').size(), 5, "Student list based on course 2 correctly fetched");
- },
-
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Check all'), // Marking all Student as checked
- () => {
- for(i = 0; i < $('input.students-check').size(); i++){
- if($('input.students-check')[i].checked == true)
- count++;
- }
-
- if(count == $('input.students-check').size())
- assert.equal($('input.students-check').size(), count, "All students marked checked");
- },
-
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Uncheck all'), // Marking all Student as unchecked
- () => {
- count = 0;
- for(i = 0; i < $('input.students-check').size(); i++){
- if(!($('input.students-check')[i].checked))
- count++;
- }
-
- if(count == $('input.students-check').size())
- assert.equal($('input.students-check').size(), count, "All students marked checked");
- },
-
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Check all'),
- () => frappe.tests.click_button('Mark Attendance'),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => {
- assert.equal($('.msgprint').text(), "Attendance has been marked successfully.", "Attendance successfully marked");
- frappe.tests.click_button('Close');
- },
-
- () => frappe.timeout(1),
- () => frappe.set_route('List', 'Student Attendance/List'),
- () => frappe.timeout(1),
- () => {
- assert.equal(cur_list.data.length, count, "Attendance list created");
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_batch_name/test_student_batch_name.js b/erpnext/education/doctype/student_batch_name/test_student_batch_name.js
deleted file mode 100644
index 6c761b8..0000000
--- a/erpnext/education/doctype/student_batch_name/test_student_batch_name.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Batch Name', function(assert){
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Student Batch Name', [
- {batch_name: 'A'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.batch_name=='A');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_category/test_student_category.js b/erpnext/education/doctype/student_category/test_student_category.js
deleted file mode 100644
index 01f50e2..0000000
--- a/erpnext/education/doctype/student_category/test_student_category.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Category', function(assert){
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Student Category', [
- {category: 'Reservation'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.name=='Reservation');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_group/test_student_group.js b/erpnext/education/doctype/student_group/test_student_group.js
deleted file mode 100644
index 4c7e47b..0000000
--- a/erpnext/education/doctype/student_group/test_student_group.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// Testing Student Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Group', function(assert){
- assert.expect(2);
- let done = assert.async();
- let group_based_on = ["test-batch-wise-group", "test-course-wise-group"];
- let tasks = [];
-
- frappe.run_serially([
- // Creating a Batch and Course based group
- () => {
- return frappe.tests.make('Student Group', [
- {academic_year: '2016-17'},
- {academic_term: '2016-17 (Semester 1)'},
- {program: "Standard Test"},
- {group_based_on: 'Batch'},
- {student_group_name: group_based_on[0]},
- {max_strength: 10},
- {batch: 'A'}
- ]);
- },
- () => {
- return frappe.tests.make('Student Group', [
- {academic_year: '2016-17'},
- {academic_term: '2016-17 (Semester 1)'},
- {program: "Standard Test"},
- {group_based_on: 'Course'},
- {student_group_name: group_based_on[1]},
- {max_strength: 10},
- {batch: 'A'},
- {course: 'Test_Sub'},
- ]);
- },
-
- // Populating the created group with Students
- () => {
- tasks = [];
- group_based_on.forEach(index => {
- tasks.push(
- () => frappe.timeout(0.5),
- () => frappe.set_route("Form", ('Student Group/' + index)),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Get Students'),
- () => frappe.timeout(1),
- () => {
- assert.equal(cur_frm.doc.students.length, 5, 'Successfully fetched list of students');
- },
- );
- });
- return frappe.run_serially(tasks);
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js
deleted file mode 100644
index fa612ba..0000000
--- a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js
+++ /dev/null
@@ -1,84 +0,0 @@
-QUnit.module('education');
-
-QUnit.test('Test: Student Group Creation Tool', function(assert){
- assert.expect(5);
- let done = assert.async();
- let instructor_code;
-
- frappe.run_serially([
- // Saving Instructor code beforehand
- () => frappe.db.get_value('Instructor', {'instructor_name': 'Instructor 1'}, 'name'),
- (instructor) => {instructor_code = instructor.message.name;},
-
- // Setting up the creation tool to generate and save Student Group
- () => frappe.set_route('Form', 'Student Group Creation Tool'),
- () => frappe.timeout(0.5),
- () => {
- cur_frm.set_value("academic_year", "2016-17");
- cur_frm.set_value("academic_term", "2016-17 (Semester 1)");
- cur_frm.set_value("program", "Standard Test");
- frappe.tests.click_button('Get Courses');
- },
- () => frappe.timeout(1),
- () => {
- let no_of_courses = $('input.grid-row-check.pull-left').size() - 1;
- assert.equal(cur_frm.doc.courses.length, no_of_courses, 'Successfully created groups using the tool');
- },
-
- () => {
- let d, grid, grid_row;
-
- for(d = 0; d < cur_frm.doc.courses.length; d++)
- {
- grid = cur_frm.get_field("courses").grid;
- grid_row = grid.get_row(d).toggle_view(true);
- if(grid_row.doc.student_group_name == 'Standard Test/A/2016-17 (Semester 1)'){
- grid_row.doc.max_strength = 10;
- grid_row.doc.student_group_name = "test-batch-wise-group-2";
- $(`.octicon.octicon-triangle-up`).click();
- continue;
- }
- else if(grid_row.doc.student_group_name == 'Test_Sub/Standard Test/2016-17 (Semester 1)'){
- grid_row.doc.max_strength = 10;
- grid_row.doc.student_group_name = "test-course-wise-group-2";
- $(`.octicon.octicon-triangle-up`).click();
- continue;
- }
- }
- },
-
- // Generating Student Group
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button("Create Student Groups"),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button("Close"),
-
- // Goin to the generated group to set up student and instructor list
- () => {
- let group_name = ['Student Group/test-batch-wise-group-2', 'Student Group/test-course-wise-group-2'];
- let tasks = [];
- group_name.forEach(index => {
- tasks.push(
- () => frappe.timeout(1),
- () => frappe.set_route("Form", index),
- () => frappe.timeout(0.5),
- () => {
- assert.equal(cur_frm.doc.students.length, 5, 'Successfully fetched list of students');
- },
- () => frappe.timeout(0.5),
- () => {
- d = cur_frm.add_child('instructors');
- d.instructor = instructor_code;
- cur_frm.save();
- },
- () => {
- assert.equal(cur_frm.doc.instructors.length, 1, 'Instructor detail stored successfully');
- },
- );
- });
- return frappe.run_serially(tasks);
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js b/erpnext/education/doctype/student_leave_application/test_student_leave_application.js
deleted file mode 100644
index 6bbf17b..0000000
--- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js
+++ /dev/null
@@ -1,69 +0,0 @@
-// Testing Attendance Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Leave Application', function(assert){
- assert.expect(4);
- let done = assert.async();
- let student_code;
- let leave_code;
- frappe.run_serially([
- () => frappe.db.get_value('Student', {'student_email_id': 'test2@testmail.com'}, 'name'),
- (student) => {student_code = student.message.name;}, // fetching student code from db
-
- () => {
- return frappe.tests.make('Student Leave Application', [
- {student: student_code},
- {from_date: '2017-08-02'},
- {to_date: '2017-08-04'},
- {mark_as_present: 0},
- {reason: "Sick Leave."}
- ]);
- },
- () => frappe.tests.click_button('Submit'), // Submitting the leave application
- () => frappe.timeout(0.7),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.7),
- () => {
- assert.equal(cur_frm.doc.docstatus, 1, "Submitted leave application");
- leave_code = frappe.get_route()[2];
- },
- () => frappe.tests.click_button('Cancel'), // Cancelling the leave application
- () => frappe.timeout(0.7),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => {assert.equal(cur_frm.doc.docstatus, 2, "Cancelled leave application");},
- () => frappe.tests.click_button('Amend'), // Amending the leave application
- () => frappe.timeout(1),
- () => {
- cur_frm.doc.mark_as_present = 1;
- cur_frm.save();
- },
- () => frappe.timeout(0.7),
- () => frappe.tests.click_button('Submit'),
- () => frappe.timeout(0.7),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.7),
- () => {assert.equal(cur_frm.doc.amended_from, leave_code, "Amended successfully");},
-
- () => frappe.timeout(0.5),
- () => {
- return frappe.tests.make('Student Leave Application', [
- {student: student_code},
- {from_date: '2017-08-07'},
- {to_date: '2017-08-09'},
- {mark_as_present: 0},
- {reason: "Sick Leave."}
- ]);
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.timeout(0.7),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.7),
- () => {
- assert.equal(cur_frm.doc.docstatus, 1, "Submitted leave application");
- leave_code = frappe.get_route()[2];
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/education/doctype/student_log/test_student_log.js b/erpnext/education/doctype/student_log/test_student_log.js
deleted file mode 100644
index 4c90c5f..0000000
--- a/erpnext/education/doctype/student_log/test_student_log.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Testing Student Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Student Log', function(assert){
- assert.expect(9);
- let done = assert.async();
- let student_code;
- frappe.run_serially([
- () => frappe.db.get_value('Student', {'student_email_id': 'test2@testmail.com'}, 'name'),
- (student) => {student_code = student.message.name;},
- () => {
- return frappe.tests.make("Student Log", [
- {student: student_code},
- {academic_year: '2016-17'},
- {academic_term: '2016-17 (Semester 1)'},
- {program: "Standard Test"},
- {date: '2017-07-31'},
- {student_batch: 'A'},
- {log: 'This is Test log.'}
- ]);
- },
- () => {
- assert.equal(cur_frm.doc.student, student_code, 'Student code was fetched properly');
- assert.equal(cur_frm.doc.student_name, 'Fname Mname Lname', 'Student name was correctly auto-fetched');
- assert.equal(cur_frm.doc.type, 'General', 'Default type selected');
- assert.equal(cur_frm.doc.academic_year, '2016-17');
- assert.equal(cur_frm.doc.academic_term, '2016-17 (Semester 1)');
- assert.equal(cur_frm.doc.program, 'Standard Test', 'Program correctly selected');
- assert.equal(cur_frm.doc.student_batch, 'A');
- assert.equal(cur_frm.doc.date, '2017-07-31');
- assert.equal(cur_frm.doc.log, 'This is Test log.');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
deleted file mode 100644
index 5efafd6..0000000
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
+++ /dev/null
@@ -1,78 +0,0 @@
-{
- "charts": [],
- "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Integrations Settings\", \"col\": 4}}]",
- "creation": "2020-07-31 10:38:54.021237",
- "docstatus": 0,
- "doctype": "Workspace",
- "for_user": "",
- "hide_custom": 0,
- "icon": "setting",
- "idx": 0,
- "label": "ERPNext Integrations Settings",
- "links": [
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Integrations Settings",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Woocommerce Settings",
- "link_count": 0,
- "link_to": "Woocommerce Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Amazon MWS Settings",
- "link_count": 0,
- "link_to": "Amazon MWS Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Plaid Settings",
- "link_count": 0,
- "link_to": "Plaid Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Exotel Settings",
- "link_count": 0,
- "link_to": "Exotel Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- }
- ],
- "modified": "2021-11-23 04:30:33.106991",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "ERPNext Integrations Settings",
- "owner": "Administrator",
- "parent_page": "",
- "public": 1,
- "restrict_to_domain": "",
- "roles": [],
- "sequence_id": 11,
- "shortcuts": [],
- "title": "ERPNext Integrations Settings"
-}
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 9ceb626..1d11f20 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -374,7 +374,7 @@
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
- "erpnext.non_profit.doctype.membership.membership.set_expired_status"
+ "erpnext.non_profit.doctype.membership.membership.set_expired_status",
"erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder"
],
"daily_long": [
diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
index 0120188..71327bf 100644
--- a/erpnext/hr/doctype/appointment_letter/appointment_letter.py
+++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
@@ -12,14 +12,15 @@
@frappe.whitelist()
def get_appointment_letter_details(template):
body = []
- intro= frappe.get_list("Appointment Letter Template",
- fields = ['introduction', 'closing_notes'],
- filters={'name': template
- })[0]
- content = frappe.get_list("Appointment Letter content",
- fields = ['title', 'description'],
- filters={'parent': template
- })
+ intro = frappe.get_list('Appointment Letter Template',
+ fields=['introduction', 'closing_notes'],
+ filters={'name': template}
+ )[0]
+ content = frappe.get_all('Appointment Letter content',
+ fields=['title', 'description'],
+ filters={'parent': template},
+ order_by='idx'
+ )
body.append(intro)
body.append({'description': content})
return body
diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.js b/erpnext/hr/doctype/appraisal/test_appraisal.js
deleted file mode 100644
index fb1354c..0000000
--- a/erpnext/hr/doctype/appraisal/test_appraisal.js
+++ /dev/null
@@ -1,57 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Expense Claim [HR]", function (assert) {
- assert.expect(3);
- let done = assert.async();
- let employee_name;
-
- frappe.run_serially([
- // Creating Appraisal
- () => frappe.set_route('List','Appraisal','List'),
- () => frappe.timeout(0.3),
- () => frappe.click_button('Make a new Appraisal'),
- () => {
- cur_frm.set_value('kra_template','Test Appraisal 1'),
- cur_frm.set_value('start_date','2017-08-21'),
- cur_frm.set_value('end_date','2017-09-21');
- },
- () => frappe.timeout(1),
- () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 1','score',4),
- () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 1','score_earned',2),
- () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 2','score',4),
- () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 2','score_earned',2),
- () => frappe.timeout(1),
- () => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
- (r) => {
- employee_name = r.message.name;
- },
-
- () => frappe.timeout(1),
- () => cur_frm.set_value('employee',employee_name),
- () => cur_frm.set_value('employee_name','Test Employee 1'),
- () => cur_frm.set_value('company','For Testing'),
- () => frappe.click_button('Calculate Total Score'),
- () => frappe.timeout(1),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => cur_frm.save(),
-
- // Submitting the Appraisal
- () => frappe.click_button('Submit'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(3),
-
- // Checking if the appraisal is correctly set for the employee
- () => {
- assert.equal('Submitted',cur_frm.get_field('status').value,
- 'Appraisal is submitted');
-
- assert.equal('Test Employee 1',cur_frm.get_field('employee_name').value,
- 'Appraisal is created for correct employee');
-
- assert.equal(4,cur_frm.get_field('total_score').value,
- 'Total score is correctly calculated');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js
deleted file mode 100644
index 3eb64e0..0000000
--- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js
+++ /dev/null
@@ -1,29 +0,0 @@
-QUnit.module('hr');
-QUnit.test("Test: Appraisal Template [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- // Job Opening creation
- () => {
- frappe.tests.make('Appraisal Template', [
- { kra_title: 'Test Appraisal 1'},
- { description: 'This is just a test'},
- { goals: [
- [
- { kra: 'Design'},
- { per_weightage: 50}
- ],
- [
- { kra: 'Code creation'},
- { per_weightage: 50}
- ]
- ]},
- ]);
- },
- () => frappe.timeout(10),
- () => {
- assert.equal('Test Appraisal 1',cur_frm.doc.kra_title, 'Appraisal name correctly set');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/attendance/test_attendance.js b/erpnext/hr/doctype/attendance/test_attendance.js
deleted file mode 100644
index b3e7fef..0000000
--- a/erpnext/hr/doctype/attendance/test_attendance.js
+++ /dev/null
@@ -1,39 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Attendance [HR]", function (assert) {
- assert.expect(4);
- let done = assert.async();
-
- frappe.run_serially([
- // test attendance creation for one employee
- () => frappe.set_route("List", "Attendance", "List"),
- () => frappe.timeout(0.5),
- () => frappe.new_doc("Attendance"),
- () => frappe.timeout(1),
- () => assert.equal("Attendance", cur_frm.doctype,
- "Form for new Attendance opened successfully."),
- // set values in form
- () => cur_frm.set_value("company", "For Testing"),
- () => {
- frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name', function(r) {
- cur_frm.set_value("employee", r.name)
- });
- },
- () => frappe.timeout(1),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- // check docstatus of attendance before submit [Draft]
- () => assert.equal("0", cur_frm.doc.docstatus,
- "attendance is currently drafted"),
- // check docstatus of attendance after submit [Present]
- () => cur_frm.savesubmit(),
- () => frappe.timeout(0.5),
- () => frappe.click_button('Yes'),
- () => assert.equal("1", cur_frm.doc.docstatus,
- "attendance is saved after submit"),
- // check if auto filled date is present day
- () => assert.equal(frappe.datetime.nowdate(), cur_frm.doc.attendance_date,
- "attendance for Present day is marked"),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/department/department.js b/erpnext/hr/doctype/department/department.js
index 7db8cfb..46cfbda 100644
--- a/erpnext/hr/doctype/department/department.js
+++ b/erpnext/hr/doctype/department/department.js
@@ -6,6 +6,15 @@
frm.set_query("parent_department", function(){
return {"filters": [["Department", "is_group", "=", 1]]};
});
+
+ frm.set_query("payroll_cost_center", function() {
+ return {
+ filters: {
+ "company": frm.doc.company,
+ "is_group": 0
+ }
+ };
+ });
},
refresh: function(frm) {
// read-only for root department
diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js
index 13b33e2..8c73e9c 100755
--- a/erpnext/hr/doctype/employee/employee.js
+++ b/erpnext/hr/doctype/employee/employee.js
@@ -47,6 +47,15 @@
}
};
});
+
+ frm.set_query("payroll_cost_center", function() {
+ return {
+ filters: {
+ "company": frm.doc.company,
+ "is_group": 0
+ }
+ };
+ });
},
onload: function (frm) {
frm.set_query("department", function() {
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 88e5ca9..a2df26c 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -68,12 +68,18 @@
self.employee_name = ' '.join(filter(lambda x: x, [self.first_name, self.middle_name, self.last_name]))
def validate_user_details(self):
- data = frappe.db.get_value('User',
- self.user_id, ['enabled', 'user_image'], as_dict=1)
- if data.get("user_image") and self.image == '':
- self.image = data.get("user_image")
- self.validate_for_enabled_user_id(data.get("enabled", 0))
- self.validate_duplicate_user_id()
+ if self.user_id:
+ data = frappe.db.get_value('User',
+ self.user_id, ['enabled', 'user_image'], as_dict=1)
+
+ if not data:
+ self.user_id = None
+ return
+
+ if data.get("user_image") and self.image == '':
+ self.image = data.get("user_image")
+ self.validate_for_enabled_user_id(data.get("enabled", 0))
+ self.validate_duplicate_user_id()
def update_nsm_model(self):
frappe.utils.nestedset.update_nsm(self)
diff --git a/erpnext/hr/doctype/employee/test_employee.js b/erpnext/hr/doctype/employee/test_employee.js
deleted file mode 100644
index 3a41458..0000000
--- a/erpnext/hr/doctype/employee/test_employee.js
+++ /dev/null
@@ -1,40 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Employee [HR]", function (assert) {
- assert.expect(4);
- let done = assert.async();
- // let today_date = frappe.datetime.nowdate();
- let employee_creation = (name, joining_date, birth_date) => {
- frappe.run_serially([
- // test employee creation
- () => {
- frappe.tests.make('Employee', [
- { employee_name: name},
- { salutation: 'Mr'},
- { company: 'For Testing'},
- { date_of_joining: joining_date},
- { date_of_birth: birth_date},
- { employment_type: 'Test Employment Type'},
- { holiday_list: 'Test Holiday List'},
- { branch: 'Test Branch'},
- { department: 'Test Department'},
- { designation: 'Test Designation'}
- ]);
- },
- () => frappe.timeout(2),
- () => {
- assert.ok(cur_frm.get_field('employee_name').value==name,
- 'Name of an Employee is correctly set');
- assert.ok(cur_frm.get_field('gender').value=='Male',
- 'Gender of an Employee is correctly set');
- },
- ]);
- };
- frappe.run_serially([
- () => employee_creation('Test Employee 1','2017-04-01','1992-02-02'),
- () => frappe.timeout(10),
- () => employee_creation('Test Employee 3','2017-04-01','1992-02-02'),
- () => frappe.timeout(10),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js
deleted file mode 100644
index 48d4344..0000000
--- a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js
+++ /dev/null
@@ -1,61 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Employee attendance tool [HR]", function (assert) {
- assert.expect(2);
- let done = assert.async();
- let today_date = frappe.datetime.nowdate();
- let date_of_attendance = frappe.datetime.add_days(today_date, -2); // previous day
-
- frappe.run_serially([
- // create employee
- () => {
- return frappe.tests.make('Employee', [
- {salutation: "Mr"},
- {employee_name: "Test Employee 2"},
- {company: "For Testing"},
- {date_of_joining: frappe.datetime.add_months(today_date, -2)}, // joined 2 month from now
- {date_of_birth: frappe.datetime.add_months(today_date, -240)}, // age is 20 years
- {employment_type: "Test Employment type"},
- {holiday_list: "Test Holiday list"},
- {branch: "Test Branch"},
- {department: "Test Department"},
- {designation: "Test Designation"}
- ]);
- },
- () => frappe.set_route("Form", "Employee Attendance Tool"),
- () => frappe.timeout(0.5),
- () => assert.equal("Employee Attendance Tool", cur_frm.doctype,
- "Form for Employee Attendance Tool opened successfully."),
- // set values in form
- () => cur_frm.set_value("date", date_of_attendance),
- () => cur_frm.set_value("branch", "Test Branch"),
- () => cur_frm.set_value("department", "Test Department"),
- () => cur_frm.set_value("company", "For Testing"),
- () => frappe.timeout(1),
- () => frappe.click_button('Check all'),
- () => frappe.click_button('Mark Present'),
- // check if attendance is marked
- () => frappe.set_route("List", "Attendance", "List"),
- () => frappe.timeout(1),
- () => {
- return frappe.call({
- method: "frappe.client.get_list",
- args: {
- doctype: "Employee",
- filters: {
- "branch": "Test Branch",
- "department": "Test Department",
- "company": "For Testing",
- "status": "Active"
- }
- },
- callback: function(r) {
- let marked_attendance = cur_list.data.filter(d => d.attendance_date == date_of_attendance);
- assert.equal(marked_attendance.length, r.message.length,
- 'all the attendance are marked for correct date');
- }
- });
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.js b/erpnext/hr/doctype/employment_type/test_employment_type.js
deleted file mode 100644
index fd7c6a1..0000000
--- a/erpnext/hr/doctype/employment_type/test_employment_type.js
+++ /dev/null
@@ -1,22 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Employment type [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // test employment type creation
- () => frappe.set_route("List", "Employment Type", "List"),
- () => frappe.new_doc("Employment Type"),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("employee_type_name", "Test Employment type"),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Employment type", cur_frm.doc.employee_type_name,
- 'name of employment type correctly saved'),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 6655563..0479457 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -171,7 +171,7 @@
['docstatus', '=', 1],
['employee', '=', frm.doc.employee],
['paid_amount', '>', 0],
- ['paid_amount', '>', 'claimed_amount']
+ ['status', '!=', 'Claimed']
]
};
});
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.js b/erpnext/hr/doctype/expense_claim/test_expense_claim.js
deleted file mode 100644
index 2529fae..0000000
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.js
+++ /dev/null
@@ -1,44 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Expense Claim [HR]", function (assert) {
- assert.expect(3);
- let done = assert.async();
- let employee_name;
- let d;
- frappe.run_serially([
- // Creating Expense Claim
- () => frappe.set_route('List','Expense Claim','List'),
- () => frappe.timeout(0.3),
- () => frappe.click_button('New'),
- () => {
- cur_frm.set_value('is_paid',1),
- cur_frm.set_value('expenses',[]),
- d = frappe.model.add_child(cur_frm.doc,'Expense Claim Detail','expenses'),
- d.expense_date = '2017-08-01',
- d.expense_type = 'Test Expense Type 1',
- d.description = 'This is just to test Expense Claim',
- d.amount = 2000,
- d.sanctioned_amount=2000,
- refresh_field('expenses');
- },
- () => frappe.timeout(1),
- () => cur_frm.set_value('employee','Test Employee 1'),
- () => cur_frm.set_value('company','For Testing'),
- () => cur_frm.set_value('payable_account','Creditors - FT'),
- () => cur_frm.set_value('cost_center','Main - FT'),
- () => cur_frm.set_value('mode_of_payment','Cash'),
- () => cur_frm.save(),
- () => frappe.click_button('Submit'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(3),
-
- // Checking if the amount is correctly reimbursed for the employee
- () => {
- assert.equal("Test Employee 1",cur_frm.doc.employee, 'Employee name set correctly');
- assert.equal(1, cur_frm.doc.is_paid, 'Expense is paid as required');
- assert.equal(2000, cur_frm.doc.total_amount_reimbursed, 'Amount is reimbursed correctly');
-
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index ec70361..2a07920 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -10,15 +10,17 @@
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
-test_records = frappe.get_test_records('Expense Claim')
test_dependencies = ['Employee']
-company_name = '_Test Company 4'
+company_name = '_Test Company 3'
class TestExpenseClaim(unittest.TestCase):
+ def tearDown(self):
+ frappe.db.rollback()
+
def test_total_expense_claim_for_project(self):
- frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
- frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
+ frappe.db.sql("""delete from `tabTask`""")
+ frappe.db.sql("""delete from `tabProject`""")
frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
project = frappe.get_doc({
@@ -37,12 +39,12 @@
task_name = task.name
payable_account = get_payable_account(company_name)
- make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", project.name, task_name)
+ make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3", project.name, task_name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200)
- expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4", project.name, task_name)
+ expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC3", project.name, task_name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700)
@@ -54,7 +56,7 @@
def test_expense_claim_status(self):
payable_account = get_payable_account(company_name)
- expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4")
+ expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3")
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
je = frappe.get_doc(je_dict)
@@ -73,7 +75,7 @@
def test_expense_claim_gl_entry(self):
payable_account = get_payable_account(company_name)
taxes = generate_taxes()
- expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4",
+ expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3",
do_not_submit=True, taxes=taxes)
expense_claim.submit()
@@ -84,9 +86,9 @@
self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [
- ['Output Tax CGST - _TC4',18.0, 0.0],
+ ['Output Tax CGST - _TC3',18.0, 0.0],
[payable_account, 0.0, 218.0],
- ["Travel Expenses - _TC4", 200.0, 0.0]
+ ["Travel Expenses - _TC3", 200.0, 0.0]
])
for gle in gl_entries:
@@ -102,7 +104,7 @@
"payable_account": payable_account,
"approval_status": "Rejected",
"expenses":
- [{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }]
+ [{"expense_type": "Travel", "default_account": "Travel Expenses - _TC3", "amount": 300, "sanctioned_amount": 200}]
})
expense_claim.submit()
diff --git a/erpnext/hr/doctype/expense_claim/test_records.json b/erpnext/hr/doctype/expense_claim/test_records.json
deleted file mode 100644
index fe51488..0000000
--- a/erpnext/hr/doctype/expense_claim/test_records.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js
deleted file mode 100644
index 3c9ed35..0000000
--- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js
+++ /dev/null
@@ -1,29 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Expense Claim Type [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- // Creating a Expense Claim Type
- () => {
- frappe.tests.make('Expense Claim Type', [
- { expense_type: 'Test Expense Type 1'},
- { description:'This is just a test'},
- { accounts: [
- [
- { company: 'For Testing'},
- { default_account: 'Rounded Off - FT'}
- ]
- ]},
- ]);
- },
- () => frappe.timeout(5),
-
- // Checking if the created type is present in the list
- () => {
- assert.equal('Test Expense Type 1', cur_frm.doc.expense_type,
- 'Expense Claim Type created successfully');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.js b/erpnext/hr/doctype/holiday_list/test_holiday_list.js
deleted file mode 100644
index ce76614..0000000
--- a/erpnext/hr/doctype/holiday_list/test_holiday_list.js
+++ /dev/null
@@ -1,42 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Holiday list [HR]", function (assert) {
- assert.expect(3);
- let done = assert.async();
- let date = frappe.datetime.add_months(frappe.datetime.nowdate(), -2); // date 2 months from now
-
- frappe.run_serially([
- // test holiday list creation
- () => frappe.set_route("List", "Holiday List", "List"),
- () => frappe.new_doc("Holiday List"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("holiday_list_name", "Test Holiday list"),
- () => cur_frm.set_value("from_date", date),
- () => cur_frm.set_value("weekly_off", "Sunday"), // holiday list for sundays
- () => frappe.click_button('Get Weekly Off Dates'),
-
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Holiday list", cur_frm.doc.holiday_list_name,
- 'name of holiday list correctly saved'),
-
- // check if holiday list contains correct days
- () => {
- var list = cur_frm.doc.holidays;
- var list_length = list.length;
- var i = 0;
- for ( ; i < list_length; i++)
- if (list[i].description != 'Sunday') break;
- assert.equal(list_length, i, "all holidays are sundays in holiday list");
- },
-
- // check if to_date is set one year from from_date
- () => {
- var date_year_later = frappe.datetime.add_days(frappe.datetime.add_months(date, 12), -1); // date after one year
- assert.equal(date_year_later, cur_frm.doc.to_date,
- "to date set correctly");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.js b/erpnext/hr/doctype/job_applicant/test_job_applicant.js
deleted file mode 100644
index 741a182..0000000
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.js
+++ /dev/null
@@ -1,28 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Job Opening [HR]", function (assert) {
- assert.expect(2);
- let done = assert.async();
-
- frappe.run_serially([
- // Job Applicant creation
- () => {
- frappe.tests.make('Job Applicant', [
- { applicant_name: 'Utkarsh Goswami'},
- { email_id: 'goswamiutkarsh0@gmail.com'},
- { job_title: 'software-developer'},
- { cover_letter: 'Highly skilled in designing, testing, and developing software.'+
- ' This is just a test.'}
- ]);
- },
- () => frappe.timeout(4),
- () => frappe.set_route('List','Job Applicant'),
- () => frappe.timeout(3),
- () => {
- assert.ok(cur_list.data.length==1, 'Job Applicant created successfully');
- assert.ok(cur_list.data[0].name=='Utkarsh Goswami - goswamiutkarsh0@gmail.com - software-developer',
- 'Correct job applicant with valid job title');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.js b/erpnext/hr/doctype/job_offer/test_job_offer.js
deleted file mode 100644
index 5339b9c..0000000
--- a/erpnext/hr/doctype/job_offer/test_job_offer.js
+++ /dev/null
@@ -1,51 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Job Offer [HR]", function (assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- // Job Offer Creation
- () => {
- frappe.tests.make('Job Offer', [
- { job_applicant: 'Utkarsh Goswami - goswamiutkarsh0@gmail.com - software-developer'},
- { applicant_name: 'Utkarsh Goswami'},
- { status: 'Accepted'},
- { designation: 'Software Developer'},
- { offer_terms: [
- [
- {offer_term: 'Responsibilities'},
- {value: 'Design, installation, testing and maintenance of software systems.'}
- ],
- [
- {offer_term: 'Department'},
- {value: 'Research & Development'}
- ],
- [
- {offer_term: 'Probationary Period'},
- {value: 'The Probation period is for 3 months.'}
- ]
- ]},
- ]);
- },
- () => frappe.timeout(10),
- () => frappe.click_button('Submit'),
- () => frappe.timeout(2),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(5),
- // To check if the fields are correctly set
- () => {
- assert.ok(cur_frm.get_field('status').value=='Accepted',
- 'Status of job offer is correct');
- assert.ok(cur_frm.get_field('designation').value=='Software Developer',
- 'Designation of applicant is correct');
- },
- () => frappe.set_route('List','Job Offer','List'),
- () => frappe.timeout(2),
- // Checking the submission of and Job Offer
- () => {
- assert.ok(cur_list.data[0].docstatus==1,'Job Offer Submitted successfully');
- },
- () => frappe.timeout(2),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.js b/erpnext/hr/doctype/job_opening/test_job_opening.js
deleted file mode 100644
index cc2f027..0000000
--- a/erpnext/hr/doctype/job_opening/test_job_opening.js
+++ /dev/null
@@ -1,26 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Job Opening [HR]", function (assert) {
- assert.expect(2);
- let done = assert.async();
-
- frappe.run_serially([
- // Job Opening creation
- () => {
- frappe.tests.make('Job Opening', [
- { job_title: 'Software Developer'},
- { description:
- 'You might be responsible for writing and coding individual'+
- ' programmes or providing an entirely new software resource.'}
- ]);
- },
- () => frappe.timeout(4),
- () => frappe.set_route('List','Job Opening'),
- () => frappe.timeout(3),
- () => {
- assert.ok(cur_list.data.length==1, 'Job Opening created successfully');
- assert.ok(cur_list.data[0].job_title=='Software Developer', 'Job title Correctly set');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
deleted file mode 100644
index d5364fc..0000000
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
+++ /dev/null
@@ -1,41 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Leave allocation [HR]", function (assert) {
- assert.expect(3);
- let done = assert.async();
- let today_date = frappe.datetime.nowdate();
-
- frappe.run_serially([
- // test creating leave alloction
- () => frappe.set_route("List", "Leave Allocation", "List"),
- () => frappe.new_doc("Leave Allocation"),
- () => frappe.timeout(1),
- () => {
- frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name', function(r) {
- cur_frm.set_value("employee", r.name)
- });
- },
- () => frappe.timeout(1),
- () => cur_frm.set_value("leave_type", "Test Leave type"),
- () => cur_frm.set_value("to_date", frappe.datetime.add_months(today_date, 2)), // for two months
- () => cur_frm.set_value("description", "This is just for testing"),
- () => cur_frm.set_value("new_leaves_allocated", 2),
- () => frappe.click_check('Add unused leaves from previous allocations'),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => cur_frm.savesubmit(),
- () => frappe.timeout(1),
- () => assert.equal("Confirm", cur_dialog.title,
- 'confirmation for leave alloction shown'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(1),
- // check auto filled from date
- () => assert.equal(today_date, cur_frm.doc.from_date,
- "from date correctly set"),
- // check for total leaves
- () => assert.equal(cur_frm.doc.unused_leaves + 2, cur_frm.doc.total_leaves_allocated,
- "total leave calculation is correctly set"),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index 46401a2..1fe9139 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -4,6 +4,7 @@
from frappe.utils import add_days, add_months, getdate, nowdate
import erpnext
+from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
@@ -13,16 +14,19 @@
def setUpClass(cls):
frappe.db.sql("delete from `tabLeave Period`")
- def test_overlapping_allocation(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
+ emp_id = make_employee("test_emp_leave_allocation@salary.com")
+ cls.employee = frappe.get_doc("Employee", emp_id)
- employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
+ def tearDown(self):
+ frappe.db.rollback()
+
+ def test_overlapping_allocation(self):
leaves = [
{
"doctype": "Leave Allocation",
"__islocal": 1,
- "employee": employee.name,
- "employee_name": employee.employee_name,
+ "employee": self.employee.name,
+ "employee_name": self.employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-10-01"),
"to_date": getdate("2015-10-31"),
@@ -32,8 +36,8 @@
{
"doctype": "Leave Allocation",
"__islocal": 1,
- "employee": employee.name,
- "employee_name": employee.employee_name,
+ "employee": self.employee.name,
+ "employee_name": self.employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-01"),
"to_date": getdate("2015-11-30"),
@@ -45,40 +49,36 @@
self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save)
def test_invalid_period(self):
- employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
-
doc = frappe.get_doc({
"doctype": "Leave Allocation",
"__islocal": 1,
- "employee": employee.name,
- "employee_name": employee.employee_name,
+ "employee": self.employee.name,
+ "employee_name": self.employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-30"),
"to_date": getdate("2015-09-1"),
"new_leaves_allocated": 5
})
- #invalid period
+ # invalid period
self.assertRaises(frappe.ValidationError, doc.save)
def test_allocated_leave_days_over_period(self):
- employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
doc = frappe.get_doc({
"doctype": "Leave Allocation",
"__islocal": 1,
- "employee": employee.name,
- "employee_name": employee.employee_name,
+ "employee": self.employee.name,
+ "employee_name": self.employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-1"),
"to_date": getdate("2015-09-30"),
"new_leaves_allocated": 35
})
- #allocated leave more than period
+
+ # allocated leave more than period
self.assertRaises(frappe.ValidationError, doc.save)
def test_carry_forward_calculation(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
- frappe.db.sql("delete from `tabLeave Ledger Entry`")
leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1)
leave_type.maximum_carry_forwarded_leaves = 10
leave_type.max_leaves_allowed = 30
@@ -86,6 +86,8 @@
# initial leave allocation = 15
leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
leave_type="_Test_CF_leave",
from_date=add_months(nowdate(), -12),
to_date=add_months(nowdate(), -1),
@@ -95,6 +97,8 @@
# carry forwarded leaves considering maximum_carry_forwarded_leaves
# new_leaves = 15, carry_forwarded = 10
leave_allocation_1 = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
leave_type="_Test_CF_leave",
carry_forward=1)
leave_allocation_1.submit()
@@ -106,6 +110,8 @@
# carry forwarded leaves considering max_leave_allowed
# max_leave_allowed = 30, new_leaves = 25, carry_forwarded = 5
leave_allocation_2 = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
leave_type="_Test_CF_leave",
carry_forward=1,
new_leaves_allocated=25)
@@ -114,8 +120,6 @@
self.assertEqual(leave_allocation_2.unused_leaves, 5)
def test_carry_forward_leaves_expiry(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
- frappe.db.sql("delete from `tabLeave Ledger Entry`")
leave_type = create_leave_type(
leave_type_name="_Test_CF_leave_expiry",
is_carry_forward=1,
@@ -124,6 +128,8 @@
# initial leave allocation
leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
leave_type="_Test_CF_leave_expiry",
from_date=add_months(nowdate(), -24),
to_date=add_months(nowdate(), -12),
@@ -131,6 +137,8 @@
leave_allocation.submit()
leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
leave_type="_Test_CF_leave_expiry",
from_date=add_days(nowdate(), -90),
to_date=add_days(nowdate(), 100),
@@ -142,6 +150,8 @@
# leave allocation with carry forward of only new leaves allocated
leave_allocation_1 = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
leave_type="_Test_CF_leave_expiry",
carry_forward=1,
from_date=add_months(nowdate(), 6),
@@ -151,9 +161,10 @@
self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated)
def test_creation_of_leave_ledger_entry_on_submit(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
-
- leave_allocation = create_leave_allocation()
+ leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name
+ )
leave_allocation.submit()
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_allocation.name))
@@ -168,10 +179,10 @@
self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_allocation.name}))
def test_leave_addition_after_submit(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
- frappe.db.sql("delete from `tabLeave Ledger Entry`")
-
- leave_allocation = create_leave_allocation()
+ leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name
+ )
leave_allocation.submit()
self.assertTrue(leave_allocation.total_leaves_allocated, 15)
leave_allocation.new_leaves_allocated = 40
@@ -179,44 +190,55 @@
self.assertTrue(leave_allocation.total_leaves_allocated, 40)
def test_leave_subtraction_after_submit(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
- frappe.db.sql("delete from `tabLeave Ledger Entry`")
- leave_allocation = create_leave_allocation()
+ leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name
+ )
leave_allocation.submit()
self.assertTrue(leave_allocation.total_leaves_allocated, 15)
leave_allocation.new_leaves_allocated = 10
leave_allocation.submit()
self.assertTrue(leave_allocation.total_leaves_allocated, 10)
- def test_against_leave_application_validation_after_submit(self):
- frappe.db.sql("delete from `tabLeave Allocation`")
- frappe.db.sql("delete from `tabLeave Ledger Entry`")
+ def test_validation_against_leave_application_after_submit(self):
+ from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
- leave_allocation = create_leave_allocation()
+ make_holiday_list()
+ frappe.db.set_value("Company", self.employee.company, "default_holiday_list", "Salary Slip Test Holiday List")
+
+ leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name
+ )
leave_allocation.submit()
self.assertTrue(leave_allocation.total_leaves_allocated, 15)
- employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
+
leave_application = frappe.get_doc({
"doctype": 'Leave Application',
- "employee": employee.name,
+ "employee": self.employee.name,
"leave_type": "_Test Leave Type",
"from_date": add_months(nowdate(), 2),
"to_date": add_months(add_days(nowdate(), 10), 2),
- "company": erpnext.get_default_company() or "_Test Company",
+ "company": self.employee.company,
"docstatus": 1,
"status": "Approved",
"leave_approver": 'test@example.com'
})
leave_application.submit()
- leave_allocation.new_leaves_allocated = 8
- leave_allocation.total_leaves_allocated = 8
+ leave_application.reload()
+
+ # allocate less leaves than the ones which are already approved
+ leave_allocation.new_leaves_allocated = leave_application.total_leave_days - 1
+ leave_allocation.total_leaves_allocated = leave_application.total_leave_days - 1
self.assertRaises(frappe.ValidationError, leave_allocation.submit)
def create_leave_allocation(**args):
args = frappe._dict(args)
- employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
- leave_allocation = frappe.get_doc({
+ emp_id = make_employee("test_emp_leave_allocation@salary.com")
+ employee = frappe.get_doc("Employee", emp_id)
+
+ return frappe.get_doc({
"doctype": "Leave Allocation",
"__islocal": 1,
"employee": args.employee or employee.name,
@@ -227,6 +249,5 @@
"carry_forward": args.carry_forward or 0,
"to_date": args.to_date or add_months(nowdate(), 12)
})
- return leave_allocation
test_dependencies = ["Employee", "Leave Type"]
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.js b/erpnext/hr/doctype/leave_application/test_leave_application.js
deleted file mode 100644
index 0866b0b..0000000
--- a/erpnext/hr/doctype/leave_application/test_leave_application.js
+++ /dev/null
@@ -1,42 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Leave application [HR]", function (assert) {
- assert.expect(4);
- let done = assert.async();
- let today_date = frappe.datetime.nowdate();
- let leave_date = frappe.datetime.add_days(today_date, 1); // leave for tomorrow
-
- frappe.run_serially([
- // test creating leave application
- () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'),
- (employee) => {
- return frappe.tests.make('Leave Application', [
- {leave_type: "Test Leave type"},
- {from_date: leave_date}, // for today
- {to_date: leave_date},
- {half_day: 1},
- {employee: employee.message.name},
- {follow_via_email: 0}
- ]);
- },
-
- () => frappe.timeout(1),
- () => frappe.click_button('Actions'),
- () => frappe.click_link('Approve'), // approve the application [as administrator]
- () => frappe.click_button('Yes'),
- () => frappe.timeout(1),
- () => assert.ok(cur_frm.doc.docstatus,
- "leave application submitted after approval"),
-
- // check auto filled posting date [today]
-
- () => assert.equal(today_date, cur_frm.doc.posting_date,
- "posting date correctly set"),
- () => frappe.set_route("List", "Leave Application", "List"),
- () => frappe.timeout(1),
- // // check approved application in list
- () => assert.deepEqual(["Test Employee 1", 1], [cur_list.data[0].employee_name, cur_list.data[0].docstatus]),
- // "leave for correct employee is submitted"),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
deleted file mode 100644
index b39601b..0000000
--- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
+++ /dev/null
@@ -1,27 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Leave block list [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
- let today_date = frappe.datetime.nowdate();
-
- frappe.run_serially([
- // test leave block list creation
- () => frappe.set_route("List", "Leave Block List", "List"),
- () => frappe.new_doc("Leave Block List"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("leave_block_list_name", "Test Leave block list"),
- () => cur_frm.set_value("company", "For Testing"),
- () => frappe.click_button('Add Row'),
- () => {
- cur_frm.fields_dict.leave_block_list_dates.grid.grid_rows[0].doc.block_date = today_date;
- cur_frm.fields_dict.leave_block_list_dates.grid.grid_rows[0].doc.reason = "Blocked leave test";
- },
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Leave block list", cur_frm.doc.leave_block_list_name,
- 'name of blocked leave list correctly saved'),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
deleted file mode 100644
index 9d37327..0000000
--- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
+++ /dev/null
@@ -1,50 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Leave control panel [HR]", function (assert) {
- assert.expect(2);
- let done = assert.async();
- let today_date = frappe.datetime.nowdate();
-
- frappe.run_serially([
- // test leave allocation using leave control panel
- () => frappe.set_route("Form", "Leave Control Panel"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("leave_type", "Test Leave type"),
- () => cur_frm.set_value("company", "For Testing"),
- () => cur_frm.set_value("employment_type", "Test Employment Type"),
- () => cur_frm.set_value("branch", "Test Branch"),
- () => cur_frm.set_value("department", "Test Department"),
- () => cur_frm.set_value("designation", "Test Designation"),
- () => cur_frm.set_value("from_date", frappe.datetime.add_months(today_date, -2)),
- () => cur_frm.set_value("to_date", frappe.datetime.add_days(today_date, -1)), // for two months [not today]
- () => cur_frm.set_value("no_of_days", 3),
- // allocate leaves
- () => frappe.click_button('Allocate'),
- () => frappe.timeout(1),
- () => assert.equal("Message", cur_dialog.title, "leave alloction message shown"),
- () => frappe.click_button('Close'),
- () => frappe.set_route("List", "Leave Allocation", "List"),
- () => frappe.timeout(1),
- () => {
- return frappe.call({
- method: "frappe.client.get_list",
- args: {
- doctype: "Employee",
- filters: {
- "branch": "Test Branch",
- "department": "Test Department",
- "company": "For Testing",
- "designation": "Test Designation",
- "status": "Active"
- }
- },
- callback: function(r) {
- let leave_allocated = cur_list.data.filter(d => d.leave_type == "Test Leave type");
- assert.equal(r.message.length, leave_allocated.length,
- 'leave allocation successfully done for all the employees');
- }
- });
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.js b/erpnext/hr/doctype/leave_type/test_leave_type.js
deleted file mode 100644
index db910cd..0000000
--- a/erpnext/hr/doctype/leave_type/test_leave_type.js
+++ /dev/null
@@ -1,22 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Leave type [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // test leave type creation
- () => frappe.set_route("List", "Leave Type", "List"),
- () => frappe.new_doc("Leave Type"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("leave_type_name", "Test Leave type"),
- () => cur_frm.set_value("max_continuous_days_allowed", "5"),
- () => frappe.click_check('Is Carry Forward'),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Leave type", cur_frm.doc.leave_type_name,
- 'leave type correctly saved'),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event.js b/erpnext/hr/doctype/training_event/tests/test_training_event.js
deleted file mode 100644
index 08031a1..0000000
--- a/erpnext/hr/doctype/training_event/tests/test_training_event.js
+++ /dev/null
@@ -1,59 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Training Event [HR]", function (assert) {
- assert.expect(5);
- let done = assert.async();
- let employee_name;
-
- frappe.run_serially([
- // Creation of Training Event
- () => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
- (r) => {
- employee_name = r.message.name;
- },
- () => {
- frappe.tests.make('Training Event', [
- { event_name: 'Test Training Event 1'},
- { location: 'Mumbai'},
- { start_time: '2017-09-01 11:00:0'},
- { end_time: '2017-09-01 17:00:0'},
- { introduction: 'This is just a test'},
- { employees: [
- [
- {employee: employee_name},
- {employee_name: 'Test Employee 1'},
- {attendance: 'Optional'}
- ]
- ]},
- ]);
- },
- () => frappe.timeout(7),
- () => frappe.click_button('Submit'),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(8),
- () => {
- // To check if the fields are correctly set
- assert.ok(cur_frm.get_field('event_name').value == 'Test Training Event 1',
- 'Event created successfully');
-
- assert.ok(cur_frm.get_field('event_status').value=='Scheduled',
- 'Status of event is correctly set');
-
- assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1',
- 'Attendee Employee is correctly set');
-
- assert.ok(cur_frm.doc.employees[0].attendance=='Optional',
- 'Attendance is correctly set');
- },
-
- () => frappe.set_route('List','Training Event','List'),
- () => frappe.timeout(2),
- // Checking the submission of Training Event
- () => {
- assert.ok(cur_list.data[0].docstatus==1,'Training Event Submitted successfully');
- },
- () => frappe.timeout(2),
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.js b/erpnext/hr/doctype/training_feedback/test_training_feedback.js
deleted file mode 100644
index 5c825ae..0000000
--- a/erpnext/hr/doctype/training_feedback/test_training_feedback.js
+++ /dev/null
@@ -1,51 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Training Feedback [HR]", function (assert) {
- assert.expect(3);
- let done = assert.async();
- let employee_name;
-
- frappe.run_serially([
- // Creating Training Feedback
- () => frappe.set_route('List','Training Feedback','List'),
- () => frappe.timeout(0.3),
- () => frappe.click_button('Make a new Training Feedback'),
- () => frappe.timeout(1),
- () => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
- (r) => {
- employee_name = r.message.name;
- },
- () => cur_frm.set_value('employee',employee_name),
- () => cur_frm.set_value('employee_name','Test Employee 1'),
- () => cur_frm.set_value('training_event','Test Training Event 1'),
- () => cur_frm.set_value('event_name','Test Training Event 1'),
- () => cur_frm.set_value('feedback','Great Experience. This is just a test.'),
- () => frappe.timeout(1),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => cur_frm.save(),
-
- // Submitting the feedback
- () => frappe.click_button('Submit'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(3),
-
- // Checking if the feedback is given by correct employee
- () => {
- assert.equal('Test Employee 1',cur_frm.get_field('employee_name').value,
- 'Feedback is given by correct employee');
-
- assert.equal('Test Training Event 1',cur_frm.get_field('training_event').value,
- 'Feedback is given for correct event');
- },
-
- () => frappe.set_route('List','Training Feedback','List'),
- () => frappe.timeout(2),
-
- // Checking the submission of Training Result
- () => {
- assert.ok(cur_list.data[0].docstatus==1,'Training Feedback Submitted successfully');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/hr/doctype/training_result_employee/test_training_result.js b/erpnext/hr/doctype/training_result_employee/test_training_result.js
deleted file mode 100644
index 3f39750..0000000
--- a/erpnext/hr/doctype/training_result_employee/test_training_result.js
+++ /dev/null
@@ -1,52 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Training Result [HR]", function (assert) {
- assert.expect(5);
- let done = assert.async();
- frappe.run_serially([
- // Creating Training Result
- () => frappe.set_route('List','Training Result','List'),
- () => frappe.timeout(0.3),
- () => frappe.click_button('Make a new Training Result'),
- () => {
- cur_frm.set_value('training_event','Test Training Event 1');
- },
- () => frappe.timeout(1),
- () => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','hours',4),
- () => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','grade','A'),
- () => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','comments','Nice Seminar'),
- () => frappe.timeout(1),
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => cur_frm.save(),
-
- // Submitting the Training Result
- () => frappe.click_button('Submit'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(4),
-
- // Checking if the fields are correctly set
- () => {
- assert.equal('Test Training Event 1',cur_frm.get_field('training_event').value,
- 'Training Result is created');
-
- assert.equal('Test Employee 1',cur_frm.doc.employees[0].employee_name,
- 'Training Result is created for correct employee');
-
- assert.equal(4,cur_frm.doc.employees[0].hours,
- 'Hours field is correctly calculated');
-
- assert.equal('A',cur_frm.doc.employees[0].grade,
- 'Grade field is correctly set');
- },
-
- () => frappe.set_route('List','Training Result','List'),
- () => frappe.timeout(2),
-
- // Checking the submission of Training Result
- () => {
- assert.ok(cur_list.data[0].docstatus==1,'Training Result Submitted successfully');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index 5979992..af26f7b 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -240,12 +240,14 @@
"label": "Repayment Schedule"
},
{
+ "allow_on_submit": 1,
"depends_on": "eval:doc.is_term_loan == 1",
"fieldname": "repayment_schedule",
"fieldtype": "Table",
"label": "Repayment Schedule",
"no_copy": 1,
- "options": "Repayment Schedule"
+ "options": "Repayment Schedule",
+ "read_only": 1
},
{
"fieldname": "section_break_17",
@@ -363,6 +365,7 @@
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index 84e0f03..f660a24 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -7,7 +7,7 @@
import frappe
from frappe import _
-from frappe.utils import add_months, flt, getdate, now_datetime, nowdate
+from frappe.utils import add_months, flt, get_last_day, getdate, now_datetime, nowdate
import erpnext
from erpnext.controllers.accounts_controller import AccountsController
@@ -62,7 +62,7 @@
self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
if self.repayment_method == "Repay Over Number of Periods":
- self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
+ self.monthly_repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods)
def check_sanctioned_amount_limit(self):
sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
@@ -99,7 +99,7 @@
"total_payment": total_payment,
"balance_loan_amount": balance_amount
})
- next_payment_date = add_months(payment_date, 1)
+ next_payment_date = add_single_month(payment_date)
payment_date = next_payment_date
def set_repayment_period(self):
@@ -211,7 +211,7 @@
if monthly_repayment_amount > loan_amount:
frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount"))
-def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest, repayment_periods):
+def get_monthly_repayment_amount(loan_amount, rate_of_interest, repayment_periods):
if rate_of_interest:
monthly_interest_rate = flt(rate_of_interest) / (12 *100)
monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate *
@@ -395,3 +395,9 @@
"value": len(applicants),
"fieldtype": "Int"
}
+
+def add_single_month(date):
+ if getdate(date) == get_last_day(date):
+ return get_last_day(add_months(date, 1))
+ else:
+ return add_months(date, 1)
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index c0f058f..1676c21 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -218,6 +218,14 @@
self.assertEqual(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid -
penalty_amount - total_interest_paid, 0))
+ # Check Repayment Entry cancel
+ repayment_entry.load_from_db()
+ repayment_entry.cancel()
+
+ loan.load_from_db()
+ self.assertEqual(loan.total_principal_paid, 0)
+ self.assertEqual(loan.total_principal_paid, 0)
+
def test_loan_closure(self):
pledge = [{
"loan_security": "Test Security 1",
@@ -295,6 +303,27 @@
self.assertEqual(amounts[0], 11250.00)
self.assertEqual(amounts[1], 78303.00)
+ def test_repayment_schedule_update(self):
+ loan = create_loan(self.applicant2, "Personal Loan", 200000, "Repay Over Number of Periods", 4,
+ applicant_type='Customer', repayment_start_date='2021-04-30', posting_date='2021-04-01')
+
+ loan.submit()
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date='2021-04-01')
+
+ process_loan_interest_accrual_for_term_loans(posting_date='2021-05-01')
+ process_loan_interest_accrual_for_term_loans(posting_date='2021-06-01')
+
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2021-06-05', 120000)
+ repayment_entry.submit()
+
+ loan.load_from_db()
+
+ self.assertEqual(flt(loan.get('repayment_schedule')[3].principal_amount, 2), 41369.83)
+ self.assertEqual(flt(loan.get('repayment_schedule')[3].interest_amount, 2), 289.59)
+ self.assertEqual(flt(loan.get('repayment_schedule')[3].total_payment, 2), 41659.41)
+ self.assertEqual(flt(loan.get('repayment_schedule')[3].balance_loan_amount, 2), 0)
+
def test_security_shortfall(self):
pledges = [{
"loan_security": "Test Security 2",
@@ -938,18 +967,18 @@
def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods,
- repayment_start_date=None, posting_date=None):
+ applicant_type=None, repayment_start_date=None, posting_date=None):
loan = frappe.get_doc({
"doctype": "Loan",
- "applicant_type": "Employee",
+ "applicant_type": applicant_type or "Employee",
"company": "_Test Company",
"applicant": applicant,
"loan_type": loan_type,
"loan_amount": loan_amount,
"repayment_method": repayment_method,
"repayment_periods": repayment_periods,
- "repayment_start_date": nowdate(),
+ "repayment_start_date": repayment_start_date or nowdate(),
"is_term_loan": 1,
"posting_date": posting_date or nowdate()
})
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index 24d8d68..a8ffcb9 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -80,7 +80,7 @@
if self.is_term_loan:
if self.repayment_method == "Repay Over Number of Periods":
- self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
+ self.repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods)
if self.repayment_method == "Repay Fixed Amount per Period":
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 93b4af9..e2d758b 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -176,20 +176,19 @@
@frappe.whitelist()
def get_disbursal_amount(loan, on_current_security_price=0):
+ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+ get_pending_principal_amount,
+ )
+
loan_details = frappe.get_value("Loan", loan, ["loan_amount", "disbursed_amount", "total_payment",
"total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan",
- "maximum_loan_amount"], as_dict=1)
+ "maximum_loan_amount", "written_off_amount"], as_dict=1)
if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan,
'status': 'Pending'}):
return 0
- if loan_details.status == 'Disbursed':
- pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
- - flt(loan_details.total_principal_paid)
- else:
- pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
- - flt(loan_details.total_principal_paid)
+ pending_principal_amount = get_pending_principal_amount(loan_details)
security_value = 0.0
if loan_details.is_secured_loan and on_current_security_price:
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index e945d49..0de073f 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -74,6 +74,39 @@
})
)
+ if self.payable_principal_amount:
+ gle_map.append(
+ self.get_gl_dict({
+ "account": self.loan_account,
+ "party_type": self.applicant_type,
+ "party": self.applicant,
+ "against": self.interest_income_account,
+ "debit": self.payable_principal_amount,
+ "debit_in_account_currency": self.interest_amount,
+ "against_voucher_type": "Loan",
+ "against_voucher": self.loan,
+ "remarks": _("Interest accrued from {0} to {1} against loan: {2}").format(
+ self.last_accrual_date, self.posting_date, self.loan),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "posting_date": self.posting_date
+ })
+ )
+
+ gle_map.append(
+ self.get_gl_dict({
+ "account": self.interest_income_account,
+ "against": self.loan_account,
+ "credit": self.payable_principal_amount,
+ "credit_in_account_currency": self.interest_amount,
+ "against_voucher_type": "Loan",
+ "against_voucher": self.loan,
+ "remarks": ("Interest accrued from {0} to {1} against loan: {2}").format(
+ self.last_accrual_date, self.posting_date, self.loan),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "posting_date": self.posting_date
+ })
+ )
+
if gle_map:
make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)
@@ -82,7 +115,10 @@
# rate of interest is 13.5 then first loan interest accural will be on '01-10-2019'
# which means interest will be accrued for 30 days which should be equal to 11095.89
def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type):
- from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
+ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+ calculate_amounts,
+ get_pending_principal_amount,
+ )
no_of_days = get_no_of_days_for_interest_accural(loan, posting_date)
precision = cint(frappe.db.get_default("currency_precision")) or 2
@@ -90,12 +126,7 @@
if no_of_days <= 0:
return
- if loan.status == 'Disbursed':
- pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
- - flt(loan.total_principal_paid) - flt(loan.written_off_amount)
- else:
- pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
- - flt(loan.total_principal_paid) - flt(loan.written_off_amount)
+ pending_principal_amount = get_pending_principal_amount(loan)
interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date)
payable_interest = interest_per_day * no_of_days
@@ -133,7 +164,7 @@
if not open_loans:
open_loans = frappe.get_all("Loan",
- fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account",
+ fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account", "loan_amount",
"is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant",
"rate_of_interest", "total_interest_payable", "written_off_amount", "total_principal_paid", "repayment_start_date"],
filters=query_filters)
@@ -190,7 +221,8 @@
AND l.is_term_loan =1
AND rs.payment_date <= %s
AND rs.is_accrued=0 {0}
- AND l.status = 'Disbursed'""".format(condition), (getdate(date)), as_dict=1)
+ AND l.status = 'Disbursed'
+ ORDER BY rs.payment_date""".format(condition), (getdate(date)), as_dict=1)
return term_loans
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 5922e4f..2abb395 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -35,9 +35,12 @@
def on_submit(self):
self.update_paid_amount()
+ self.update_repayment_schedule()
self.make_gl_entries()
def on_cancel(self):
+ self.check_future_accruals()
+ self.update_repayment_schedule(cancel=1)
self.mark_as_unpaid()
self.ignore_linked_doctypes = ['GL Entry']
self.make_gl_entries(cancel=1)
@@ -90,7 +93,7 @@
def book_unaccrued_interest(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2
- if self.total_interest_paid > self.interest_payable:
+ if flt(self.total_interest_paid, precision) > flt(self.interest_payable, precision):
if not self.is_term_loan:
# get last loan interest accrual date
last_accrual_date = get_last_accrual_date(self.against_loan)
@@ -121,7 +124,18 @@
})
def update_paid_amount(self):
- loan = frappe.get_doc("Loan", self.against_loan)
+ loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid',
+ 'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'total_interest_payable',
+ 'written_off_amount'], as_dict=1)
+
+ loan.update({
+ 'total_amount_paid': loan.total_amount_paid + self.amount_paid,
+ 'total_principal_paid': loan.total_principal_paid + self.principal_amount_paid
+ })
+
+ pending_principal_amount = get_pending_principal_amount(loan)
+ if not loan.is_secured_loan and pending_principal_amount <= 0:
+ loan.update({'status': 'Loan Closure Requested'})
for payment in self.repayment_details:
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
@@ -130,17 +144,31 @@
WHERE name = %s""",
(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
- frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
- WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
- loan.total_principal_paid + self.principal_amount_paid, self.against_loan))
+ frappe.db.sql(""" UPDATE `tabLoan`
+ SET total_amount_paid = %s, total_principal_paid = %s, status = %s
+ WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status,
+ self.against_loan))
update_shortfall_status(self.against_loan, self.principal_amount_paid)
def mark_as_unpaid(self):
- loan = frappe.get_doc("Loan", self.against_loan)
+ loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid',
+ 'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'total_interest_payable',
+ 'written_off_amount'], as_dict=1)
no_of_repayments = len(self.repayment_details)
+ loan.update({
+ 'total_amount_paid': loan.total_amount_paid - self.amount_paid,
+ 'total_principal_paid': loan.total_principal_paid - self.principal_amount_paid
+ })
+
+ if loan.status == 'Loan Closure Requested':
+ if loan.disbursed_amount >= loan.loan_amount:
+ loan['status'] = 'Disbursed'
+ else:
+ loan['status'] = 'Partially Disbursed'
+
for payment in self.repayment_details:
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
SET paid_principal_amount = `paid_principal_amount` - %s,
@@ -154,12 +182,20 @@
lia_doc = frappe.get_doc('Loan Interest Accrual', payment.loan_interest_accrual)
lia_doc.cancel()
- frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
- WHERE name = %s """, (loan.total_amount_paid - self.amount_paid,
- loan.total_principal_paid - self.principal_amount_paid, self.against_loan))
+ frappe.db.sql(""" UPDATE `tabLoan`
+ SET total_amount_paid = %s, total_principal_paid = %s, status = %s
+ WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan))
- if loan.status == "Loan Closure Requested":
- frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
+ def check_future_accruals(self):
+ future_accrual_date = frappe.db.get_value("Loan Interest Accrual", {"posting_date": (">", self.posting_date),
+ "docstatus": 1, "loan": self.against_loan}, 'posting_date')
+
+ if future_accrual_date:
+ frappe.throw("Cannot cancel. Interest accruals already processed till {0}".format(get_datetime(future_accrual_date)))
+
+ def update_repayment_schedule(self, cancel=0):
+ if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount:
+ regenerate_repayment_schedule(self.against_loan, cancel)
def allocate_amounts(self, repayment_details):
self.set('repayment_details', [])
@@ -182,50 +218,93 @@
interest_paid -= self.total_penalty_paid
- total_interest_paid = 0
- # interest_paid = self.amount_paid - self.principal_amount_paid - self.penalty_amount
+ if self.is_term_loan:
+ interest_paid, updated_entries = self.allocate_interest_amount(interest_paid, repayment_details)
+ self.allocate_principal_amount_for_term_loans(interest_paid, repayment_details, updated_entries)
+ else:
+ interest_paid, updated_entries = self.allocate_interest_amount(interest_paid, repayment_details)
+ self.allocate_excess_payment_for_demand_loans(interest_paid, repayment_details)
+
+ def allocate_interest_amount(self, interest_paid, repayment_details):
+ updated_entries = {}
+ self.total_interest_paid = 0
+ idx = 1
if interest_paid > 0:
for lia, amounts in repayment_details.get('pending_accrual_entries', []).items():
- if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid:
+ interest_amount = 0
+ if amounts['interest_amount'] <= interest_paid:
interest_amount = amounts['interest_amount']
- paid_principal = amounts['payable_principal_amount']
- self.principal_amount_paid += paid_principal
- interest_paid -= (interest_amount + paid_principal)
+ self.total_interest_paid += interest_amount
+ interest_paid -= interest_amount
elif interest_paid:
if interest_paid >= amounts['interest_amount']:
interest_amount = amounts['interest_amount']
- paid_principal = interest_paid - interest_amount
- self.principal_amount_paid += paid_principal
+ self.total_interest_paid += interest_amount
interest_paid = 0
else:
interest_amount = interest_paid
+ self.total_interest_paid += interest_amount
interest_paid = 0
- paid_principal=0
- total_interest_paid += interest_amount
- self.append('repayment_details', {
- 'loan_interest_accrual': lia,
- 'paid_interest_amount': interest_amount,
- 'paid_principal_amount': paid_principal
- })
+ if interest_amount:
+ self.append('repayment_details', {
+ 'loan_interest_accrual': lia,
+ 'paid_interest_amount': interest_amount,
+ 'paid_principal_amount': 0
+ })
+ updated_entries[lia] = idx
+ idx += 1
+ return interest_paid, updated_entries
+
+ def allocate_principal_amount_for_term_loans(self, interest_paid, repayment_details, updated_entries):
+ if interest_paid > 0:
+ for lia, amounts in repayment_details.get('pending_accrual_entries', []).items():
+ paid_principal = 0
+ if amounts['payable_principal_amount'] <= interest_paid:
+ paid_principal = amounts['payable_principal_amount']
+ self.principal_amount_paid += paid_principal
+ interest_paid -= paid_principal
+ elif interest_paid:
+ if interest_paid >= amounts['payable_principal_amount']:
+ paid_principal = amounts['payable_principal_amount']
+ self.principal_amount_paid += paid_principal
+ interest_paid = 0
+ else:
+ paid_principal = interest_paid
+ self.principal_amount_paid += paid_principal
+ interest_paid = 0
+
+ if updated_entries.get(lia):
+ idx = updated_entries.get(lia)
+ self.get('repayment_details')[idx-1].paid_principal_amount += paid_principal
+ else:
+ self.append('repayment_details', {
+ 'loan_interest_accrual': lia,
+ 'paid_interest_amount': 0,
+ 'paid_principal_amount': paid_principal
+ })
+
+ if interest_paid > 0:
+ self.principal_amount_paid += interest_paid
+
+ def allocate_excess_payment_for_demand_loans(self, interest_paid, repayment_details):
if repayment_details['unaccrued_interest'] and interest_paid > 0:
# no of days for which to accrue interest
# Interest can only be accrued for an entire day and not partial
if interest_paid > repayment_details['unaccrued_interest']:
interest_paid -= repayment_details['unaccrued_interest']
- total_interest_paid += repayment_details['unaccrued_interest']
+ self.total_interest_paid += repayment_details['unaccrued_interest']
else:
# get no of days for which interest can be paid
per_day_interest = get_per_day_interest(self.pending_principal_amount,
self.rate_of_interest, self.posting_date)
no_of_days = cint(interest_paid/per_day_interest)
- total_interest_paid += no_of_days * per_day_interest
+ self.total_interest_paid += no_of_days * per_day_interest
interest_paid -= no_of_days * per_day_interest
- self.total_interest_paid = total_interest_paid
if interest_paid > 0:
self.principal_amount_paid += interest_paid
@@ -361,6 +440,76 @@
else:
return None, 0
+def regenerate_repayment_schedule(loan, cancel=0):
+ from erpnext.loan_management.doctype.loan.loan import (
+ add_single_month,
+ get_monthly_repayment_amount,
+ )
+
+ loan_doc = frappe.get_doc('Loan', loan)
+ next_accrual_date = None
+ accrued_entries = 0
+ last_repayment_amount = 0
+ last_balance_amount = 0
+
+ for term in reversed(loan_doc.get('repayment_schedule')):
+ if not term.is_accrued:
+ next_accrual_date = term.payment_date
+ loan_doc.remove(term)
+ else:
+ accrued_entries += 1
+ if not last_repayment_amount:
+ last_repayment_amount = term.total_payment
+ if not last_balance_amount:
+ last_balance_amount = term.balance_loan_amount
+
+ loan_doc.save()
+
+ balance_amount = get_pending_principal_amount(loan_doc)
+
+ if loan_doc.repayment_method == 'Repay Fixed Amount per Period':
+ monthly_repayment_amount = flt(balance_amount/len(loan_doc.get('repayment_schedule')) - accrued_entries)
+ else:
+ if not cancel:
+ monthly_repayment_amount = get_monthly_repayment_amount(balance_amount,
+ loan_doc.rate_of_interest, loan_doc.repayment_periods - accrued_entries)
+ else:
+ monthly_repayment_amount = last_repayment_amount
+ balance_amount = last_balance_amount
+
+ payment_date = next_accrual_date
+
+ while(balance_amount > 0):
+ interest_amount = flt(balance_amount * flt(loan_doc.rate_of_interest) / (12*100))
+ principal_amount = monthly_repayment_amount - interest_amount
+ balance_amount = flt(balance_amount + interest_amount - monthly_repayment_amount)
+ if balance_amount < 0:
+ principal_amount += balance_amount
+ balance_amount = 0.0
+
+ total_payment = principal_amount + interest_amount
+ loan_doc.append("repayment_schedule", {
+ "payment_date": payment_date,
+ "principal_amount": principal_amount,
+ "interest_amount": interest_amount,
+ "total_payment": total_payment,
+ "balance_loan_amount": balance_amount
+ })
+ next_payment_date = add_single_month(payment_date)
+ payment_date = next_payment_date
+
+ loan_doc.save()
+
+def get_pending_principal_amount(loan):
+ if loan.status in ('Disbursed', 'Closed') or loan.disbursed_amount >= loan.loan_amount:
+ pending_principal_amount = flt(loan.total_payment) - flt(loan.total_principal_paid) \
+ - flt(loan.total_interest_payable) - flt(loan.written_off_amount)
+ else:
+ pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_principal_paid) \
+ - flt(loan.total_interest_payable) - flt(loan.written_off_amount)
+
+ return pending_principal_amount
+
# This function returns the amounts that are payable at the time of loan repayment based on posting date
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
@@ -408,12 +557,7 @@
if due_date and not final_due_date:
final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
- if against_loan_doc.status in ('Disbursed', 'Closed') or against_loan_doc.disbursed_amount >= against_loan_doc.loan_amount:
- pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
- - against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
- else:
- pending_principal_amount = against_loan_doc.disbursed_amount - against_loan_doc.total_principal_paid \
- - against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
+ pending_principal_amount = get_pending_principal_amount(against_loan_doc)
unaccrued_interest = 0
if due_date:
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index bff9d5c..4567374 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -27,6 +27,9 @@
d.idx, frappe.bold(d.loan_security)))
def validate_unpledge_qty(self):
+ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+ get_pending_principal_amount,
+ )
from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import (
get_ltv_ratio,
)
@@ -43,15 +46,10 @@
"valid_upto": (">=", get_datetime())
}, as_list=1))
- loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+ loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid', 'loan_amount',
'total_interest_payable', 'written_off_amount', 'disbursed_amount', 'status'], as_dict=1)
- if loan_details.status == 'Disbursed':
- pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
- - flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
- else:
- pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
- - flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
+ pending_principal_amount = get_pending_principal_amount(loan_details)
security_value = 0
unpledge_qty_map = {}
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.js b/erpnext/manufacturing/doctype/bom/test_bom.js
deleted file mode 100644
index 98a9198..0000000
--- a/erpnext/manufacturing/doctype/bom/test_bom.js
+++ /dev/null
@@ -1,63 +0,0 @@
-QUnit.test("test: item", function (assert) {
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // test item creation
- () => frappe.set_route("List", "Item"),
-
- // Create a BOM for a laptop
- () => frappe.tests.make(
- "BOM", [
- {item: "Laptop"},
- {quantity: 1},
- {with_operations: 1},
- {company: "For Testing"},
- {operations: [
- [
- {operation: "Assemble CPU"},
- {time_in_mins: 60},
- ],
- [
- {operation: "Assemble Keyboard"},
- {time_in_mins: 30},
- ],
- [
- {operation: "Assemble Screen"},
- {time_in_mins: 30},
- ]
- ]},
- {scrap_items: [
- [
- {item_code: "Scrap item"}
- ]
- ]},
- {items: [
- [
- {item_code: "CPU"},
- {qty: 1}
- ],
- [
- {item_code: "Keyboard"},
- {qty: 1}
- ],
- [
- {item_code: "Screen"},
- {qty: 1}
- ]
- ]},
- ]
- ),
- () => cur_frm.savesubmit(),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(1),
-
- () => {
- assert.ok(cur_frm.doc.operating_cost + cur_frm.doc.raw_material_cost -
- cur_frm.doc.scrap_material_cost == cur_frm.doc.total_cost, 'Total_Cost calculated correctly');
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/manufacturing/doctype/operation/test_operation.js b/erpnext/manufacturing/doctype/operation/test_operation.js
deleted file mode 100644
index fd7783f..0000000
--- a/erpnext/manufacturing/doctype/operation/test_operation.js
+++ /dev/null
@@ -1,49 +0,0 @@
-QUnit.test("test: operation", function (assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- // test operation creation
- () => frappe.set_route("List", "Operation"),
-
- // Create a Keyboard operation
- () => {
- return frappe.tests.make(
- "Operation", [
- {__newname: "Assemble Keyboard"},
- {workstation: "Keyboard assembly workstation"}
- ]
- );
- },
- () => frappe.timeout(3),
- () => {
- assert.ok(cur_frm.docname.includes('Assemble Keyboard'),
- 'Assemble Keyboard created successfully');
- assert.ok(cur_frm.doc.workstation.includes('Keyboard assembly workstation'),
- 'Keyboard assembly workstation was linked successfully');
- },
-
- // Create a Screen operation
- () => {
- return frappe.tests.make(
- "Operation", [
- {__newname: 'Assemble Screen'},
- {workstation: "Screen assembly workstation"}
- ]
- );
- },
- () => frappe.timeout(3),
-
- // Create a CPU operation
- () => {
- return frappe.tests.make(
- "Operation", [
- {__newname: 'Assemble CPU'},
- {workstation: "CPU assembly workstation"}
- ]
- );
- },
- () => frappe.timeout(3),
-
- () => done()
- ]);
-});
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.js b/erpnext/manufacturing/doctype/work_order/test_work_order.js
deleted file mode 100644
index 1e224eb..0000000
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.js
+++ /dev/null
@@ -1,130 +0,0 @@
-QUnit.test("test: work order", function (assert) {
- assert.expect(25);
- let done = assert.async();
- let laptop_quantity = 5;
- let items = ["CPU", "Keyboard", "Screen"];
- let operation_items = ["CPU", "Keyboard", "Screen"];
- let click_make = () => {
- let element = $(`.btn-primary:contains("Make"):visible`);
- if(!element.length) {
- throw `did not find any button containing 'Make'`;
- }
- element.click();
- return frappe.timeout(1);
- };
-
- frappe.run_serially([
- // test work order
- () => frappe.set_route("List", "Work Order", "List"),
- () => frappe.timeout(3),
-
- // Create a laptop work order
- () => {
- return frappe.tests.make('Work Order', [
- {production_item: 'Laptop'},
- {company: 'For Testing'},
- {qty: laptop_quantity},
- {scrap_warehouse: "Laptop Scrap Warehouse - FT"},
- {wip_warehouse: "Work In Progress - FT"},
- {fg_warehouse: "Finished Goods - FT"}
- ]);
- },
- () => frappe.timeout(3),
- () => {
- assert.equal(cur_frm.doc.planned_operating_cost, cur_frm.doc.total_operating_cost,
- "Total and Planned Cost is equal");
- assert.equal(cur_frm.doc.planned_operating_cost, cur_frm.doc.total_operating_cost,
- "Total and Planned Cost is equal");
-
- items.forEach(function(item, index) {
- assert.equal(item, cur_frm.doc.required_items[index].item_code, `Required item ${item} added`);
- assert.equal("Stores - FT", cur_frm.doc.required_items[index].source_warehouse, `Item ${item} warhouse verified`);
- assert.equal("5", cur_frm.doc.required_items[index].required_qty, `Item ${item} quantity verified`);
- });
-
- operation_items.forEach(function(operation_item, index) {
- assert.equal(`Assemble ${operation_item}`, cur_frm.doc.operations[index].operation,
- `Operation ${operation_item} added`);
- assert.equal(`${operation_item} assembly workstation`, cur_frm.doc.operations[index].workstation,
- `Workstation ${operation_item} linked`);
- });
- },
-
- // Submit the work order
- () => cur_frm.savesubmit(),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(2.5),
-
- // Confirm the work order timesheet, save and submit it
- () => frappe.click_link("TS-00"),
- () => frappe.timeout(1),
- () => frappe.click_button("Submit"),
- () => frappe.timeout(1),
- () => frappe.click_button("Yes"),
- () => frappe.timeout(2.5),
-
- // Start the work order process
- () => frappe.set_route("List", "Work Order", "List"),
- () => frappe.timeout(2),
- () => frappe.click_link("Laptop"),
- () => frappe.timeout(1),
- () => frappe.click_button("Start"),
- () => frappe.timeout(0.5),
- () => click_make(),
- () => frappe.timeout(1),
- () => frappe.click_button("Save"),
- () => frappe.timeout(0.5),
-
- () => {
- assert.equal(cur_frm.doc.total_outgoing_value, cur_frm.doc.total_incoming_value,
- "Total incoming and outgoing cost is equal");
- assert.equal(cur_frm.doc.total_outgoing_value, "99000",
- "Outgoing cost is correct"); // Price of each item x5
- },
- // Submit for work
- () => frappe.click_button("Submit"),
- () => frappe.timeout(0.5),
- () => frappe.click_button("Yes"),
- () => frappe.timeout(0.5),
-
- // Finish the work order by sending for manufacturing
- () => frappe.set_route("List", "Work Order"),
- () => frappe.timeout(1),
- () => frappe.click_link("Laptop"),
- () => frappe.timeout(1),
-
- () => {
- assert.ok(frappe.tests.is_visible("5 items in progress", 'p'), "Work order initiated");
- assert.ok(frappe.tests.is_visible("Finish"), "Finish button visible");
- },
-
- () => frappe.click_button("Finish"),
- () => frappe.timeout(0.5),
- () => click_make(),
- () => {
- assert.equal(cur_frm.doc.total_incoming_value, "105700",
- "Incoming cost is correct "+cur_frm.doc.total_incoming_value); // Price of each item x5, values are in INR
- assert.equal(cur_frm.doc.total_outgoing_value, "99000",
- "Outgoing cost is correct"); // Price of each item x5, values are in INR
- assert.equal(cur_frm.doc.total_incoming_value - cur_frm.doc.total_outgoing_value, cur_frm.doc.value_difference,
- "Value difference is correct"); // Price of each item x5, values are in INR
- },
- () => frappe.click_button("Save"),
- () => frappe.timeout(1),
- () => frappe.click_button("Submit"),
- () => frappe.timeout(1),
- () => frappe.click_button("Yes"),
- () => frappe.timeout(1),
-
- // Manufacturing finished
- () => frappe.set_route("List", "Work Order", "List"),
- () => frappe.timeout(1),
- () => frappe.click_link("Laptop"),
- () => frappe.timeout(1),
-
- () => assert.ok(frappe.tests.is_visible("5 items produced", 'p'), "Work order completed"),
-
- () => done()
- ]);
-});
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 86c687f..9926b15 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -199,8 +199,6 @@
# no change in reserved / projected
self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production),
cint(bin1_on_start_production.reserved_qty_for_production))
- self.assertEqual(cint(bin1_on_end_production.projected_qty),
- cint(bin1_on_end_production.projected_qty))
def test_backflush_qty_for_overpduction_manufacture(self):
cancel_stock_entry = []
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.js b/erpnext/manufacturing/doctype/workstation/test_workstation.js
deleted file mode 100644
index 1df53d0..0000000
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.js
+++ /dev/null
@@ -1,89 +0,0 @@
-QUnit.test("test: workstation", function (assert) {
- assert.expect(9);
- let done = assert.async();
- let elec_rate = 50;
- let rent = 100;
- let consumable_rate = 20;
- let labour_rate = 500;
- frappe.run_serially([
- // test workstation creation
- () => frappe.set_route("List", "Workstation"),
-
- // Create a keyboard workstation
- () => frappe.tests.make(
- "Workstation", [
- {workstation_name: "Keyboard assembly workstation"},
- {hour_rate_electricity: elec_rate},
- {hour_rate_rent: rent},
- {hour_rate_consumable: consumable_rate},
- {hour_rate_labour: labour_rate},
- {working_hours: [
- [
- {enabled: 1},
- {start_time: '11:00:00'},
- {end_time: '18:00:00'}
- ]
- ]}
- ]
- ),
- () => {
- assert.ok(cur_frm.doc.workstation_name.includes('Keyboard assembly workstation'),
- 'Keyboard assembly workstation created successfully');
- assert.equal(cur_frm.doc.hour_rate_electricity, elec_rate,
- 'electricity rate set correctly');
- assert.equal(cur_frm.doc.hour_rate_rent, rent,
- 'rent set correctly');
- assert.equal(cur_frm.doc.hour_rate_consumable, consumable_rate,
- 'consumable rate set correctly');
- assert.equal(cur_frm.doc.hour_rate_labour, labour_rate,
- 'labour rate set correctly');
- assert.equal(cur_frm.doc.working_hours[0].enabled, 1,
- 'working hours enabled');
- assert.ok(cur_frm.doc.working_hours[0].start_time.includes('11:00:0'),
- 'start time set correctly');
- assert.ok(cur_frm.doc.working_hours[0].end_time.includes('18:00:0'),
- 'end time set correctly');
- assert.ok(cur_frm.doc.hour_rate_electricity+cur_frm.doc.hour_rate_rent+
- cur_frm.doc.hour_rate_consumable+cur_frm.doc.hour_rate_labour==
- cur_frm.doc.hour_rate, 'Net hour rate set correctly');
- },
-
- // Create a Screen workstation
- () => frappe.tests.make(
- "Workstation", [
- {workstation_name: "Screen assembly workstation"},
- {hour_rate_electricity: elec_rate},
- {hour_rate_rent: rent},
- {hour_rate_consumable: consumable_rate},
- {hour_rate_labour: labour_rate},
- {working_hours: [
- [
- {enabled: 1},
- {start_time: '11:00:00'},
- {end_time: '18:00:00'}
- ]
- ]}
- ]
- ),
-
- // Create a CPU workstation
- () => frappe.tests.make(
- "Workstation", [
- {workstation_name: "CPU assembly workstation"},
- {hour_rate_electricity: elec_rate},
- {hour_rate_rent: rent},
- {hour_rate_consumable: consumable_rate},
- {hour_rate_labour: labour_rate},
- {working_hours: [
- [
- {enabled: 1},
- {start_time: '11:00:00'},
- {end_time: '18:00:00'}
- ]
- ]}
- ]
- ),
-
- () => done()
- ]);
-});
diff --git a/erpnext/non_profit/doctype/donor/test_donor.js b/erpnext/non_profit/doctype/donor/test_donor.js
deleted file mode 100644
index e478b34..0000000
--- a/erpnext/non_profit/doctype/donor/test_donor.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Donor", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(3);
-
- frappe.run_serially([
- // insert a new Member
- () => frappe.tests.make('Donor', [
- // values to be set
- {donor_name: 'Test Donor'},
- {donor_type: 'Test Organization'},
- {email: 'test@example.com'}
- ]),
- () => {
- assert.equal(cur_frm.doc.donor_name, 'Test Donor');
- assert.equal(cur_frm.doc.donor_type, 'Test Organization');
- assert.equal(cur_frm.doc.email, 'test@example.com');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/grant_application/test_grant_application.js b/erpnext/non_profit/doctype/grant_application/test_grant_application.js
deleted file mode 100644
index 47230a5..0000000
--- a/erpnext/non_profit/doctype/grant_application/test_grant_application.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Grant Application", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(4);
-
- frappe.run_serially([
- // insert a new Member
- () => frappe.tests.make('Grant Application', [
- // values to be set
- {applicant_name: 'Test Organization'},
- {contact_person:'Test Applicant'},
- {email: 'test@example.com'},
- {grant_description:'Test message'},
- {amount: 150000}
- ]),
- () => {
- assert.equal(cur_frm.doc.applicant_name, 'Test Organization');
- assert.equal(cur_frm.doc.contact_person, 'Test Applicant');
- assert.equal(cur_frm.doc.email, 'test@example.com');
- assert.equal(cur_frm.doc.amount, 150000);
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/member/test_member.js b/erpnext/non_profit/doctype/member/test_member.js
deleted file mode 100644
index f7cca97..0000000
--- a/erpnext/non_profit/doctype/member/test_member.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Member", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
-
- frappe.run_serially([
- // insert a new Member
- () => frappe.tests.make('Member', [
- // values to be set
- {member_name: 'Test Member'},
- {membership_type: 'Gold'},
- {email: 'test@example.com'}
- ]),
- () => {
- assert.equal(cur_frm.doc.membership_type, 'Gold');
- assert.equal(cur_frm.doc.email, 'test@example.com');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index beb38e2..f9b295a 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -409,7 +409,7 @@
def set_expired_status():
frappe.db.sql("""
UPDATE
- `tabMembership` SET `status` = 'Expired'
+ `tabMembership` SET `membership_status` = 'Expired'
WHERE
- `status` not in ('Cancelled') AND `to_date` < %s
+ `membership_status` not in ('Cancelled') AND `to_date` < %s
""", (nowdate()))
diff --git a/erpnext/non_profit/doctype/membership_type/test_membership_type.js b/erpnext/non_profit/doctype/membership_type/test_membership_type.js
deleted file mode 100644
index 6440df8..0000000
--- a/erpnext/non_profit/doctype/membership_type/test_membership_type.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Membership Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
-
- frappe.run_serially([
- // insert a new Member
- () => frappe.tests.make('Membership Type', [
- // values to be set
- {membership_type: 'Gold'},
- {amount:50000}
- ]),
- () => {
- assert.equal(cur_frm.doc.membership_type, 'Gold');
- assert.equal(cur_frm.doc.amount, '50000');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/volunteer/test_volunteer.js b/erpnext/non_profit/doctype/volunteer/test_volunteer.js
deleted file mode 100644
index 45eb281..0000000
--- a/erpnext/non_profit/doctype/volunteer/test_volunteer.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Volunteer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(4);
-
- frappe.run_serially([
- // insert a new Member
- () => frappe.tests.make('Volunteer', [
- // values to be set
- {volunteer_name: 'Test Volunteer'},
- {volunteer_type:'Test Work'},
- {email:'test@example.com'},
- {'availability': 'Weekends'},
- {volunteer_skills:[
- [
- {'volunteer_skills': 'Fundraiser'},
- ]
- ]},
- ]),
- () => {
- assert.equal(cur_frm.doc.volunteer_name, 'Test Volunteer');
- assert.equal(cur_frm.doc.volunteer_type, 'Test Work');
- assert.equal(cur_frm.doc.email, 'test@example.com');
- assert.equal(cur_frm.doc.availability, 'Weekends');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js b/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js
deleted file mode 100644
index 08baaf0..0000000
--- a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Volunteer Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
-
- frappe.run_serially([
- // insert a new Member
- () => {
- return frappe.tests.make('Volunteer Type', [
- // values to be set
- {__newname: 'Test Work'},
- {amount: 500}
- ]);
- },
- () => {
- assert.equal(cur_frm.doc.name, 'Test Work');
- assert.equal(cur_frm.doc.amount, 500);
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index d9cedab..deeeeb7 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -317,4 +317,7 @@
erpnext.patches.v14_0.migrate_crm_settings
erpnext.patches.v13_0.rename_ksa_qr_field
erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021
-erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
\ No newline at end of file
+erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
+erpnext.patches.v13_0.update_tax_category_for_rcm
+execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
+erpnext.patches.v14_0.set_payroll_cost_centers
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
index 08006ad..c7771a5 100644
--- a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
+++ b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
@@ -22,4 +22,5 @@
delivery_settings = frappe.get_doc("Delivery Settings")
delivery_settings.dispatch_template = _("Dispatch Notification")
+ delivery_settings.flags.ignore_links = True
delivery_settings.save()
diff --git a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
index d157aad..d4fbded 100644
--- a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
+++ b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
@@ -97,6 +97,8 @@
'itc_central_tax': 0,
'itc_cess_amount': 0
})
+ if not gst_accounts:
+ continue
if d.account_head in gst_accounts.get('igst_account'):
amount_map[d.parent]['itc_integrated_tax'] += d.amount
diff --git a/erpnext/patches/v12_0/update_bom_in_so_mr.py b/erpnext/patches/v12_0/update_bom_in_so_mr.py
index 37d850f..132f3bd 100644
--- a/erpnext/patches/v12_0/update_bom_in_so_mr.py
+++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py
@@ -6,7 +6,7 @@
frappe.reload_doc("selling", "doctype", "sales_order_item")
for doctype in ["Sales Order", "Material Request"]:
- condition = " and child_doc.stock_qty > child_doc.produced_qty"
+ condition = " and child_doc.stock_qty > child_doc.produced_qty and doc.per_delivered < 100"
if doctype == "Material Request":
condition = " and doc.per_ordered < 100 and doc.material_request_type = 'Manufacture'"
@@ -15,5 +15,6 @@
child_doc.bom_no = item.default_bom
WHERE
child_doc.item_code = item.name and child_doc.docstatus < 2
+ and child_doc.parent = doc.name
and item.default_bom is not null and item.default_bom != '' {cond}
""".format(doc = doctype, cond = condition))
diff --git a/erpnext/patches/v13_0/add_default_interview_notification_templates.py b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
index 0208ca9..6b5de52 100644
--- a/erpnext/patches/v13_0/add_default_interview_notification_templates.py
+++ b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
@@ -32,4 +32,5 @@
hr_settings = frappe.get_doc('HR Settings')
hr_settings.interview_reminder_template = _('Interview Reminder')
hr_settings.feedback_reminder_notification_template = _('Interview Feedback Reminder')
+ hr_settings.flags.ignore_links = True
hr_settings.save()
diff --git a/erpnext/patches/v13_0/update_tax_category_for_rcm.py b/erpnext/patches/v13_0/update_tax_category_for_rcm.py
new file mode 100644
index 0000000..7af2366
--- /dev/null
+++ b/erpnext/patches/v13_0/update_tax_category_for_rcm.py
@@ -0,0 +1,31 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+from erpnext.regional.india import states
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ create_custom_fields({
+ 'Tax Category': [
+ dict(fieldname='is_inter_state', label='Is Inter State',
+ fieldtype='Check', insert_after='disabled', print_hide=1),
+ dict(fieldname='is_reverse_charge', label='Is Reverse Charge', fieldtype='Check',
+ insert_after='is_inter_state', print_hide=1),
+ dict(fieldname='tax_category_column_break', fieldtype='Column Break',
+ insert_after='is_reverse_charge'),
+ dict(fieldname='gst_state', label='Source State', fieldtype='Select',
+ options='\n'.join(states), insert_after='company')
+ ]
+ }, update=True)
+
+ tax_category = frappe.qb.DocType("Tax Category")
+
+ frappe.qb.update(tax_category).set(
+ tax_category.is_reverse_charge, 1
+ ).where(
+ tax_category.name.isin(['Reverse Charge Out-State', 'Reverse Charge In-State'])
+ ).run()
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
index 8b1752b..120182a 100644
--- a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
+++ b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
@@ -24,4 +24,5 @@
hr_settings = frappe.get_doc("HR Settings")
hr_settings.exit_questionnaire_notification_template = template
+ hr_settings.flags.ignore_links = True
hr_settings.save()
diff --git a/erpnext/patches/v14_0/set_payroll_cost_centers.py b/erpnext/patches/v14_0/set_payroll_cost_centers.py
new file mode 100644
index 0000000..89b305b
--- /dev/null
+++ b/erpnext/patches/v14_0/set_payroll_cost_centers.py
@@ -0,0 +1,32 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc('payroll', 'doctype', 'employee_cost_center')
+ frappe.reload_doc('payroll', 'doctype', 'salary_structure_assignment')
+
+ employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"])
+
+ employee_cost_center = {}
+ for d in employees:
+ cost_center = d.payroll_cost_center
+ if not cost_center and d.department:
+ cost_center = frappe.get_cached_value("Department", d.department, "payroll_cost_center")
+
+ if cost_center:
+ employee_cost_center.setdefault(d.name, cost_center)
+
+ salary_structure_assignments = frappe.get_all("Salary Structure Assignment",
+ filters = {"docstatus": ["!=", 2]},
+ fields=["name", "employee"])
+
+ for d in salary_structure_assignments:
+ cost_center = employee_cost_center.get(d.employee)
+ if cost_center:
+ assignment = frappe.get_doc("Salary Structure Assignment", d.name)
+ if not assignment.get("payroll_cost_centers"):
+ assignment.append("payroll_cost_centers", {
+ "cost_center": cost_center,
+ "percentage": 100
+ })
+ assignment.save()
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_cost_center/__init__.py b/erpnext/payroll/doctype/employee_cost_center/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_cost_center/__init__.py
diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json
new file mode 100644
index 0000000..8fed9f7
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json
@@ -0,0 +1,43 @@
+{
+ "actions": [],
+ "creation": "2021-12-23 12:44:38.389283",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "cost_center",
+ "percentage"
+ ],
+ "fields": [
+ {
+ "allow_on_submit": 1,
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Cost Center",
+ "options": "Cost Center",
+ "reqd": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "percentage",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Percentage (%)",
+ "non_negative": 1,
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-12-23 17:39:03.410924",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Cost Center",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py
new file mode 100644
index 0000000..6c5be97
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class EmployeeCostCenter(Document):
+ pass
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 84c59a2..5bb32cf 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -7,6 +7,7 @@
from frappe import _
from frappe.desk.reportview import get_filters_cond, get_match_cond
from frappe.model.document import Document
+from frappe.query_builder.functions import Coalesce
from frappe.utils import (
DATE_FORMAT,
add_days,
@@ -157,11 +158,20 @@
Returns list of salary slips based on selected criteria
"""
- ss_list = frappe.db.sql("""
- select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
- where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s
- and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s
- """, (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict)
+ ss = frappe.qb.DocType("Salary Slip")
+ ss_list = (
+ frappe.qb.from_(ss)
+ .select(ss.name, ss.salary_structure)
+ .where(
+ (ss.docstatus == ss_status)
+ & (ss.start_date >= self.start_date)
+ & (ss.end_date <= self.end_date)
+ & (ss.payroll_entry == self.name)
+ & ((ss.journal_entry.isnull()) | (ss.journal_entry == ""))
+ & (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet)
+ )
+ ).run(as_dict=as_dict)
+
return ss_list
@frappe.whitelist()
@@ -190,13 +200,20 @@
def get_salary_components(self, component_type):
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
+
if salary_slips:
- salary_components = frappe.db.sql("""
- select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center
- from `tabSalary Slip` ss, `tabSalary Detail` ssd
- where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s)
- """ % (component_type, ', '.join(['%s']*len(salary_slips))),
- tuple([d.name for d in salary_slips]), as_dict=True)
+ ss = frappe.qb.DocType("Salary Slip")
+ ssd = frappe.qb.DocType("Salary Detail")
+ salary_components = (
+ frappe.qb.from_(ss)
+ .join(ssd)
+ .on(ss.name == ssd.parent)
+ .select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee)
+ .where(
+ (ssd.parentfield == component_type)
+ & (ss.name.isin(tuple([d.name for d in salary_slips])))
+ )
+ ).run(as_dict=True)
return salary_components
@@ -204,18 +221,49 @@
salary_components = self.get_salary_components(component_type)
if salary_components:
component_dict = {}
+ self.employee_cost_centers = {}
for item in salary_components:
+ employee_cost_centers = self.get_payroll_cost_centers_for_employee(item.employee, item.salary_structure)
+
add_component_to_accrual_jv_entry = True
if component_type == "earnings":
- is_flexible_benefit, only_tax_impact = frappe.db.get_value("Salary Component", item['salary_component'], ['is_flexible_benefit', 'only_tax_impact'])
+ is_flexible_benefit, only_tax_impact = \
+ frappe.get_cached_value("Salary Component",item['salary_component'], ['is_flexible_benefit', 'only_tax_impact'])
if is_flexible_benefit == 1 and only_tax_impact ==1:
add_component_to_accrual_jv_entry = False
+
if add_component_to_accrual_jv_entry:
- component_dict[(item.salary_component, item.payroll_cost_center)] \
- = component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount)
+ for cost_center, percentage in employee_cost_centers.items():
+ amount_against_cost_center = flt(item.amount) * percentage / 100
+ component_dict[(item.salary_component, cost_center)] \
+ = component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center
+
account_details = self.get_account(component_dict = component_dict)
return account_details
+ def get_payroll_cost_centers_for_employee(self, employee, salary_structure):
+ if not self.employee_cost_centers.get(employee):
+ ss_assignment_name = frappe.db.get_value("Salary Structure Assignment",
+ {"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, 'name')
+
+ if ss_assignment_name:
+ cost_centers = dict(frappe.get_all("Employee Cost Center", {"parent": ss_assignment_name},
+ ["cost_center", "percentage"], as_list=1))
+ if not cost_centers:
+ default_cost_center, department = frappe.get_cached_value("Employee", employee, ["payroll_cost_center", "department"])
+ if not default_cost_center and department:
+ default_cost_center = frappe.get_cached_value("Department", department, "payroll_cost_center")
+ if not default_cost_center:
+ default_cost_center = self.cost_center
+
+ cost_centers = {
+ default_cost_center: 100
+ }
+
+ self.employee_cost_centers.setdefault(employee, cost_centers)
+
+ return self.employee_cost_centers.get(employee, {})
+
def get_account(self, component_dict = None):
account_dict = {}
for key, amount in component_dict.items():
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js
deleted file mode 100644
index d24f243..0000000
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js
+++ /dev/null
@@ -1,62 +0,0 @@
-QUnit.module('HR');
-
-QUnit.test("test: Payroll Entry", function (assert) {
- assert.expect(5);
- let done = assert.async();
- let employees, docname;
-
- frappe.run_serially([
- () => {
- return frappe.tests.make('Payroll Entry', [
- {company: 'For Testing'},
- {posting_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
- {payroll_frequency: 'Monthly'},
- {cost_center: 'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
- ]);
- },
-
- () => frappe.timeout(1),
- () => {
- assert.equal(cur_frm.doc.company, 'For Testing');
- assert.equal(cur_frm.doc.posting_date, frappe.datetime.add_days(frappe.datetime.nowdate(), 0));
- assert.equal(cur_frm.doc.cost_center, 'Main - FT');
- },
- () => frappe.click_button('Get Employee Details'),
- () => {
- employees = cur_frm.doc.employees.length;
- docname = cur_frm.doc.name;
- },
-
- () => frappe.click_button('Submit'),
- () => frappe.timeout(1),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(5),
-
- () => frappe.click_button('View Salary Slip'),
- () => frappe.timeout(2),
- () => assert.equal(cur_list.data.length, employees),
-
- () => frappe.set_route('Form', 'Payroll Entry', docname),
- () => frappe.timeout(2),
- () => frappe.click_button('Submit Salary Slip'),
- () => frappe.click_button('Yes'),
- () => frappe.timeout(5),
-
- () => frappe.click_button('Close'),
- () => frappe.timeout(1),
-
- () => frappe.click_button('View Salary Slip'),
- () => frappe.timeout(2),
- () => {
- let count = 0;
- for(var i = 0; i < employees; i++) {
- if(cur_list.data[i].docstatus == 1){
- count++;
- }
- }
- assert.equal(count, employees, "Salary Slip submitted for all employees");
- },
-
- () => done()
- ]);
-});
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index c6f3897..4f097fa 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -120,8 +120,7 @@
employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
department="cc - _TC", company="_Test Company")
- employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC",
- department="cc - _TC", company="_Test Company")
+ employee2 = make_employee("test_employee2@example.com", department="cc - _TC", company="_Test Company")
if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
create_account(account_name="_Test Payroll Payable",
@@ -132,8 +131,26 @@
frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
"_Test Payroll Payable - _TC")
currency=frappe.db.get_value("Company", "_Test Company", "default_currency")
+
make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False)
- make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
+ ss = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
+
+ # update cost centers in salary structure assignment for employee2
+ ssa = frappe.db.get_value("Salary Structure Assignment",
+ {"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, 'name')
+
+ ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa)
+ ssa_doc.payroll_cost_centers = []
+ ssa_doc.append("payroll_cost_centers", {
+ "cost_center": "_Test Cost Center - _TC",
+ "percentage": 60
+ })
+ ssa_doc.append("payroll_cost_centers", {
+ "cost_center": "_Test Cost Center 2 - _TC",
+ "percentage": 40
+ })
+
+ ssa_doc.save()
dates = get_start_end_dates('Monthly', nowdate())
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
@@ -148,10 +165,10 @@
""", je)
expected_je = (
('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
- ('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0),
- ('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0),
- ('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0),
- ('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0)
+ ('Salary - _TC', '_Test Cost Center - _TC', 124800.0, 0.0),
+ ('Salary - _TC', '_Test Cost Center 2 - _TC', 31200.0, 0.0),
+ ('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 320.0),
+ ('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 80.0)
)
self.assertEqual(je_entries, expected_je)
diff --git a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js
deleted file mode 100644
index 092cbd8..0000000
--- a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js
+++ /dev/null
@@ -1,61 +0,0 @@
-QUnit.module('HR');
-
-QUnit.test("test: Set Salary Components", function (assert) {
- assert.expect(5);
- let done = assert.async();
-
- frappe.run_serially([
- () => frappe.set_route('Form', 'Salary Component', 'Leave Encashment'),
- () => {
- var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
- row.company = 'For Testing';
- row.account = 'Salary - FT';
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'),
-
- () => frappe.set_route('Form', 'Salary Component', 'Basic'),
- () => {
- var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
- row.company = 'For Testing';
- row.account = 'Salary - FT';
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'),
-
- () => frappe.set_route('Form', 'Salary Component', 'Income Tax'),
- () => {
- var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
- row.company = 'For Testing';
- row.account = 'Salary - FT';
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'),
-
- () => frappe.set_route('Form', 'Salary Component', 'Arrear'),
- () => {
- var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
- row.company = 'For Testing';
- row.account = 'Salary - FT';
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'),
-
- () => frappe.set_route('Form', 'Company', 'For Testing'),
- () => cur_frm.set_value('default_payroll_payable_account', 'Payroll Payable - FT'),
- () => cur_frm.save(),
- () => frappe.timeout(2),
- () => assert.equal(cur_frm.doc.default_payroll_payable_account, 'Payroll Payable - FT'),
-
- () => done()
-
- ]);
-});
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
index 7a80e69..4e40e13 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.json
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -12,7 +12,6 @@
"department",
"designation",
"branch",
- "payroll_cost_center",
"column_break1",
"status",
"journal_entry",
@@ -463,15 +462,6 @@
"read_only": 1
},
{
- "fetch_from": "employee.payroll_cost_center",
- "fetch_if_empty": 1,
- "fieldname": "payroll_cost_center",
- "fieldtype": "Link",
- "label": "Payroll Cost Center",
- "options": "Cost Center",
- "read_only": 1
- },
- {
"fieldname": "mode_of_payment",
"fieldtype": "Select",
"label": "Mode Of Payment",
@@ -647,7 +637,7 @@
"idx": 9,
"is_submittable": 1,
"links": [],
- "modified": "2021-10-08 11:47:47.098248",
+ "modified": "2021-12-23 11:47:47.098248",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
deleted file mode 100644
index a47eba1..0000000
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
+++ /dev/null
@@ -1,55 +0,0 @@
-QUnit.test("test salary slip", function(assert) {
- assert.expect(6);
- let done = assert.async();
- let employee_name;
-
- let salary_slip = (ename) => {
- frappe.run_serially([
- () => frappe.db.get_value('Employee', {'employee_name': ename}, 'name'),
- (r) => {
- employee_name = r.message.name;
- },
- () => {
- // Creating a salary slip for a employee
- frappe.tests.make('Salary Slip', [
- { employee: employee_name}
- ]);
- },
- () => frappe.timeout(3),
- () => {
- // To check if all the calculations are correctly done
- if(ename === 'Test Employee 1')
- {
- assert.ok(cur_frm.doc.gross_pay==24000,
- 'Gross amount for first employee is correctly calculated');
- assert.ok(cur_frm.doc.total_deduction==4800,
- 'Deduction amount for first employee is correctly calculated');
- assert.ok(cur_frm.doc.net_pay==19200,
- 'Net amount for first employee is correctly calculated');
- }
- if(ename === 'Test Employee 3')
- {
- assert.ok(cur_frm.doc.gross_pay==28800,
- 'Gross amount for second employee is correctly calculated');
- assert.ok(cur_frm.doc.total_deduction==5760,
- 'Deduction amount for second employee is correctly calculated');
- assert.ok(cur_frm.doc.net_pay==23040,
- 'Net amount for second employee is correctly calculated');
- }
- },
- ]);
- };
- frappe.run_serially([
- () => salary_slip('Test Employee 1'),
- () => frappe.timeout(6),
- () => salary_slip('Test Employee 3'),
- () => frappe.timeout(5),
- () => frappe.set_route('List', 'Salary Slip', 'List'),
- () => frappe.timeout(2),
- () => {$('.list-row-checkbox').click();},
- () => frappe.timeout(2),
- () => frappe.click_button('Delete'),
- () => frappe.click_button('Yes'),
- () => done()
- ]);
-});
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 3052a2b..6e8fae0 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -171,6 +171,7 @@
salary_slip.end_date = month_end_date
salary_slip.save()
salary_slip.submit()
+ salary_slip.reload()
no_of_days = self.get_no_of_days()
days_in_month = no_of_days[0]
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py
index ae83c04..4cbf948 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py
@@ -167,15 +167,12 @@
def postprocess(source, target):
if employee:
employee_details = frappe.db.get_value("Employee", employee,
- ["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1)
+ ["employee_name", "branch", "designation", "department"], as_dict=1)
target.employee = employee
target.employee_name = employee_details.employee_name
target.branch = employee_details.branch
target.designation = employee_details.designation
target.department = employee_details.department
- target.payroll_cost_center = employee_details.payroll_cost_center
- if not target.payroll_cost_center and target.department:
- target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center")
target.run_method('process_salary_structure', for_preview=for_preview)
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.js b/erpnext/payroll/doctype/salary_structure/test_salary_structure.js
deleted file mode 100644
index 542fa50..0000000
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.js
+++ /dev/null
@@ -1,78 +0,0 @@
-QUnit.test("test Salary Structure", function(assert) {
- assert.expect(7);
- let done = assert.async();
- let employee_name1;
-
- frappe.run_serially([
- () => frappe.db.get_value('Employee', {'employee_name': "Test Employee 1"}, 'name',
- (r) => {
- employee_name1 = r.name;
- }
- ),
- () => frappe.timeout(5),
- () => frappe.db.get_value('Employee', {'employee_name': "Test Employee 3"}, 'name',
- (r) => {
- // Creating Salary Structure for employees);
- return frappe.tests.make('Salary Structure', [
- { __newname: 'Test Salary Structure'},
- { company: 'For Testing'},
- { payroll_frequency: 'Monthly'},
- { employees: [
- [
- {employee: employee_name1},
- {from_date: '2017-07-01'},
- {base: 25000}
- ],
- [
- {employee: r.name},
- {from_date: '2017-07-01'},
- {base: 30000}
- ]
- ]},
- { earnings: [
- [
- {salary_component: 'Basic'},
- {formula: 'base * .80'}
- ],
- [
- {salary_component: 'Leave Encashment'},
- {formula: 'B * .20'}
- ]
- ]},
- { deductions: [
- [
- {salary_component: 'Income Tax'},
- {formula: '(B+LE) * .20'}
- ]
- ]},
- { payment_account: 'CASH - FT'},
- ]);
- }
- ),
- () => frappe.timeout(15),
- () => {
- // To check if all the fields are correctly set
- assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1',
- 'Employee 1 name correctly set');
-
- assert.ok(cur_frm.doc.employees[1].employee_name=='Test Employee 3',
- 'Employee 2 name correctly set');
-
- assert.ok(cur_frm.doc.employees[0].base==25000,
- 'Base value for first employee is correctly set');
-
- assert.ok(cur_frm.doc.employees[1].base==30000,
- 'Base value for second employee is correctly set');
-
- assert.ok(cur_frm.doc.earnings[0].formula.includes('base * .80'),
- 'Formula for earnings as Basic is correctly set');
-
- assert.ok(cur_frm.doc.earnings[1].formula.includes('B * .20'),
- 'Formula for earnings as Leave Encashment is correctly set');
-
- assert.ok(cur_frm.doc.deductions[0].formula.includes('(B+LE) * .20'),
- 'Formula for deductions as Income Tax is correctly set');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
index 6cd897e..220bfbf 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
@@ -40,28 +40,29 @@
}
}
});
+
+ frm.set_query("cost_center", "payroll_cost_centers", function() {
+ return {
+ filters: {
+ "company": frm.doc.company,
+ "is_group": 0
+ }
+ };
+ });
},
employee: function(frm) {
- if(frm.doc.employee){
+ if (frm.doc.employee) {
frappe.call({
- method: "frappe.client.get_value",
- args:{
- doctype: "Employee",
- fieldname: "company",
- filters:{
- name: frm.doc.employee
- }
- },
+ method: "set_payroll_cost_centers",
+ doc: frm.doc,
callback: function(data) {
- if(data.message){
- frm.set_value("company", data.message.company);
- }
+ refresh_field("payroll_cost_centers");
}
});
}
- else{
- frm.set_value("company", null);
+ else {
+ frm.set_value("payroll_cost_centers", []);
}
},
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
index c8b98e5..197ab5f 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
@@ -22,7 +22,9 @@
"base",
"column_break_9",
"variable",
- "amended_from"
+ "amended_from",
+ "section_break_17",
+ "payroll_cost_centers"
],
"fields": [
{
@@ -90,7 +92,8 @@
},
{
"fieldname": "section_break_7",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Base & Variable"
},
{
"fieldname": "base",
@@ -141,14 +144,29 @@
"fieldtype": "Link",
"label": "Payroll Payable Account",
"options": "Account"
+ },
+ {
+ "collapsible": 1,
+ "depends_on": "employee",
+ "fieldname": "section_break_17",
+ "fieldtype": "Section Break",
+ "label": "Payroll Cost Centers"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "payroll_cost_centers",
+ "fieldtype": "Table",
+ "label": "Cost Centers",
+ "options": "Employee Cost Center"
}
],
"is_submittable": 1,
"links": [],
- "modified": "2021-03-31 22:44:46.267974",
+ "modified": "2021-12-23 17:28:09.794444",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Structure Assignment",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -193,6 +211,7 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"title_field": "employee_name",
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
index e1ff9ca..8359478 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -5,7 +5,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import getdate
+from frappe.utils import flt, getdate
class DuplicateAssignment(frappe.ValidationError): pass
@@ -15,6 +15,10 @@
self.validate_dates()
self.validate_income_tax_slab()
self.set_payroll_payable_account()
+ if not self.get("payroll_cost_centers"):
+ self.set_payroll_cost_centers()
+
+ self.validate_cost_center_distribution()
def validate_dates(self):
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
@@ -51,6 +55,30 @@
"Company", self.company, "default_currency"), "is_group": 0})
self.payroll_payable_account = payroll_payable_account
+ @frappe.whitelist()
+ def set_payroll_cost_centers(self):
+ self.payroll_cost_centers = []
+ default_payroll_cost_center = self.get_payroll_cost_center()
+ if default_payroll_cost_center:
+ self.append("payroll_cost_centers", {
+ "cost_center": default_payroll_cost_center,
+ "percentage": 100
+ })
+
+ def get_payroll_cost_center(self):
+ payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center")
+ if not payroll_cost_center and self.department:
+ payroll_cost_center = frappe.db.get_value("Department", self.department, "payroll_cost_center")
+
+ return payroll_cost_center
+
+ def validate_cost_center_distribution(self):
+ if self.get("payroll_cost_centers"):
+ total_percentage = sum([flt(d.percentage) for d in self.get("payroll_cost_centers", [])])
+ if total_percentage != 100:
+ frappe.throw(_("Total percentage against cost centers should be 100"))
+
+
def get_assigned_salary_structure(employee, on_date):
if not employee or not on_date:
return None
@@ -64,6 +92,7 @@
})
return salary_structure[0][0] if salary_structure else None
+
@frappe.whitelist()
def get_employee_currency(employee):
employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency')
diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.js b/erpnext/projects/doctype/activity_type/test_activity_type.js
deleted file mode 100644
index 62be972..0000000
--- a/erpnext/projects/doctype/activity_type/test_activity_type.js
+++ /dev/null
@@ -1,21 +0,0 @@
-QUnit.test("test: Activity Type", function (assert) {
- // number of asserts
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // insert a new Activity Type
- () => frappe.set_route("List", "Activity Type", "List"),
- () => frappe.new_doc("Activity Type"),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("activity_type", "Test Activity"),
- () => frappe.click_button('Save'),
- () => frappe.timeout(1),
- () => {
- assert.equal(cur_frm.doc.name,"Test Activity");
- },
- () => done()
- ]);
-});
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 31460f6..4f19bbd 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -59,22 +59,16 @@
frm.trigger('show_dashboard');
}
- frm.events.set_buttons(frm);
+ frm.trigger("set_custom_buttons");
},
- set_buttons: function(frm) {
+ set_custom_buttons: function(frm) {
if (!frm.is_new()) {
frm.add_custom_button(__('Duplicate Project with Tasks'), () => {
frm.events.create_duplicate(frm);
- });
+ }, __("Actions"));
- frm.add_custom_button(__('Completed'), () => {
- frm.events.set_status(frm, 'Completed');
- }, __('Set Status'));
-
- frm.add_custom_button(__('Cancelled'), () => {
- frm.events.set_status(frm, 'Cancelled');
- }, __('Set Status'));
+ frm.trigger("set_project_status_button");
if (frappe.model.can_read("Task")) {
@@ -83,7 +77,7 @@
"project": frm.doc.name
};
frappe.set_route("List", "Task", "Gantt");
- });
+ }, __("View"));
frm.add_custom_button(__("Kanban Board"), () => {
frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
@@ -91,13 +85,35 @@
}).then(() => {
frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
});
- });
+ }, __("View"));
}
}
},
+ set_project_status_button: function(frm) {
+ frm.add_custom_button(__('Set Project Status'), () => {
+ let d = new frappe.ui.Dialog({
+ "title": __("Set Project Status"),
+ "fields": [
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "label": "Status",
+ "reqd": 1,
+ "options": "Completed\nCancelled",
+ },
+ ],
+ primary_action: function() {
+ frm.events.set_status(frm, d.get_values().status);
+ d.hide();
+ },
+ primary_action_label: __("Set Project Status")
+ }).show();
+ }, __("Actions"));
+ },
+
create_duplicate: function(frm) {
return new Promise(resolve => {
frappe.prompt('Project Name', (data) => {
@@ -117,7 +133,9 @@
set_status: function(frm, status) {
frappe.confirm(__('Set Project and all Tasks to status {0}?', [status.bold()]), () => {
frappe.xcall('erpnext.projects.doctype.project.project.set_project_status',
- {project: frm.doc.name, status: status}).then(() => { /* page will auto reload */ });
+ {project: frm.doc.name, status: status}).then(() => {
+ frm.reload_doc();
+ });
});
},
diff --git a/erpnext/projects/doctype/task/tests/test_task.js b/erpnext/projects/doctype/task/tests/test_task.js
deleted file mode 100644
index 8a1a5bf..0000000
--- a/erpnext/projects/doctype/task/tests/test_task.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Task", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
-
- frappe.run_serially([
- // insert a new Task
- () => frappe.tests.make('Task', [
- // values to be set
- {subject: 'new task'}
- ]),
- () => {
- assert.equal(cur_frm.doc.status, 'Open');
- assert.equal(cur_frm.doc.priority, 'Low');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/projects/doctype/task/tests/test_task_tree.js b/erpnext/projects/doctype/task/tests/test_task_tree.js
deleted file mode 100644
index 27dccbf..0000000
--- a/erpnext/projects/doctype/task/tests/test_task_tree.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Task Tree", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(4);
-
- frappe.run_serially([
- // insert a new Task
- () => frappe.set_route('Tree', 'Task'),
- () => frappe.timeout(0.5),
-
- // Checking adding child without selecting any Node
- () => frappe.tests.click_button('New'),
- () => frappe.timeout(0.5),
- () => {assert.equal($(`.msgprint`).text(), "Select a group node first.", "Error message success");},
- () => frappe.tests.click_button('Close'),
- () => frappe.timeout(0.5),
-
- // Creating child nodes
- () => frappe.tests.click_link('All Tasks'),
- () => frappe.map_group.make('Test-1'),
- () => frappe.map_group.make('Test-3', 1),
- () => frappe.timeout(1),
- () => frappe.tests.click_link('Test-3'),
- () => frappe.map_group.make('Test-4', 0),
-
- // Checking Edit button
- () => frappe.timeout(0.5),
- () => frappe.tests.click_link('Test-1'),
- () => frappe.tests.click_button('Edit'),
- () => frappe.timeout(1),
- () => frappe.db.get_value('Task', {'subject': 'Test-1'}, 'name'),
- (task) => {assert.deepEqual(frappe.get_route(), ["Form", "Task", task.message.name], "Edit route checks");},
-
- // Deleting child Node
- () => frappe.set_route('Tree', 'Task'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_link('Test-1'),
- () => frappe.tests.click_button('Delete'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
-
- // Deleting Group Node that has child nodes in it
- () => frappe.timeout(0.5),
- () => frappe.tests.click_link('Test-3'),
- () => frappe.tests.click_button('Delete'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => {assert.equal(cur_dialog.title, 'Message', 'Error thrown correctly');},
- () => frappe.tests.click_button('Close'),
-
- // Add multiple child tasks
- () => frappe.tests.click_link('Test-3'),
- () => frappe.timeout(0.5),
- () => frappe.click_button('Add Multiple'),
- () => frappe.timeout(1),
- () => cur_dialog.set_value('tasks', 'Test-6\nTest-7'),
- () => frappe.timeout(0.5),
- () => frappe.click_button('Submit'),
- () => frappe.timeout(2),
- () => frappe.click_button('Expand All'),
- () => frappe.timeout(1),
- () => {
- let count = $(`a:contains("Test-6"):visible`).length + $(`a:contains("Test-7"):visible`).length;
- assert.equal(count, 2, "Multiple Tasks added successfully");
- },
-
- () => done()
- ]);
-});
-
-frappe.map_group = {
- make:function(subject, is_group = 0){
- return frappe.run_serially([
- () => frappe.click_button('Add Child'),
- () => frappe.timeout(1),
- () => cur_dialog.set_value('is_group', is_group),
- () => cur_dialog.set_value('subject', subject),
- () => frappe.click_button('Create New'),
- () => frappe.timeout(1.5)
- ]);
- }
-};
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index 0415690..1eb3d0d 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -25,6 +25,7 @@
self.timesheet = make_timesheet(emp, is_billable=1)
self.salary_slip = make_salary_slip(self.timesheet.name)
+ self.salary_slip.start_date = self.timesheet.start_date
holidays = self.salary_slip.get_holidays_for_employee(date, date)
if holidays:
@@ -41,8 +42,8 @@
def test_project_profitability(self):
filters = {
'company': '_Test Company',
- 'start_date': add_days(getdate(), -3),
- 'end_date': getdate()
+ 'start_date': add_days(self.timesheet.start_date, -3),
+ 'end_date': self.timesheet.start_date
}
report = execute(filters)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 773d53c..3791741 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -680,7 +680,7 @@
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
- // check if child doctype is Sales Order Item/Qutation Item and calculate the rate
+ // check if child doctype is Sales Order Item/Quotation Item and calculate the rate
if (in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Purchase Receipt Item"]), cdt)
this.apply_pricing_rule_on_item(item);
else
@@ -1582,25 +1582,27 @@
_set_values_for_item_list(children) {
var me = this;
- var price_list_rate_changed = false;
var items_rule_dict = {};
for(var i=0, l=children.length; i<l; i++) {
- var d = children[i];
+ var d = children[i] ;
+ let item_row = frappe.get_doc(d.doctype, d.name);
var existing_pricing_rule = frappe.model.get_value(d.doctype, d.name, "pricing_rules");
for(var k in d) {
var v = d[k];
if (["doctype", "name"].indexOf(k)===-1) {
if(k=="price_list_rate") {
- if(flt(v) != flt(d.price_list_rate)) price_list_rate_changed = true;
+ item_row['rate'] = v;
}
if (k !== 'free_item_data') {
- frappe.model.set_value(d.doctype, d.name, k, v);
+ item_row[k] = v;
}
}
}
+ frappe.model.round_floats_in(item_row, ["price_list_rate", "discount_percentage"]);
+
// if pricing rule set as blank from an existing value, apply price_list
if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) {
me.apply_price_list(frappe.get_doc(d.doctype, d.name));
@@ -1617,9 +1619,10 @@
}
}
+ me.frm.refresh_field('items');
me.apply_rule_on_other_items(items_rule_dict);
- if(!price_list_rate_changed) me.calculate_taxes_and_totals();
+ me.calculate_taxes_and_totals();
}
apply_rule_on_other_items(args) {
diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js
index b635adc..b7d880a 100644
--- a/erpnext/public/js/queries.js
+++ b/erpnext/public/js/queries.js
@@ -83,6 +83,13 @@
};
},
+ dispatch_address_query: function(doc) {
+ return {
+ query: 'frappe.contacts.doctype.address.address.address_query',
+ filters: { link_doctype: 'Company', link_name: doc.company || '' }
+ };
+ },
+
supplier_filter: function(doc) {
if(!doc.supplier) {
frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "supplier", doc.name))]));
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 5865424..c0dcb70 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -277,8 +277,10 @@
inter_state_gst_field = [
dict(fieldname='is_inter_state', label='Is Inter State',
fieldtype='Check', insert_after='disabled', print_hide=1),
+ dict(fieldname='is_reverse_charge', label='Is Reverse Charge', fieldtype='Check',
+ insert_after='is_inter_state', print_hide=1),
dict(fieldname='tax_category_column_break', fieldtype='Column Break',
- insert_after='is_inter_state'),
+ insert_after='is_reverse_charge'),
dict(fieldname='gst_state', label='Source State', fieldtype='Select',
options='\n'.join(states), insert_after='company')
]
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index fd3ec3c..215b483 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -67,7 +67,8 @@
frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN."))
def validate_tax_category(doc, method):
- if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}):
+ if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state,
+ 'is_reverse_charge': doc.is_reverse_charge}):
if doc.is_inter_state:
frappe.throw(_("Inter State tax category for GST State {0} already exists").format(doc.gst_state))
else:
@@ -264,7 +265,7 @@
def get_tax_template(master_doctype, company, is_inter_state, state_code):
tax_categories = frappe.get_all('Tax Category', fields = ['name', 'is_inter_state', 'gst_state'],
- filters = {'is_inter_state': is_inter_state})
+ filters = {'is_inter_state': is_inter_state, 'is_reverse_charge': 0})
default_tax = ''
diff --git a/erpnext/restaurant/doctype/restaurant/test_restaurant.js b/erpnext/restaurant/doctype/restaurant/test_restaurant.js
deleted file mode 100644
index 8fe4e7b..0000000
--- a/erpnext/restaurant/doctype/restaurant/test_restaurant.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Restaurant", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(2);
- let customer = {
- "Test Customer 1": [
- {customer_name: "Test Customer 1"}
- ],
- "Test Customer 2": [
- {customer_name: "Test Customer 2"}
- ]
- };
-
- frappe.run_serially([
- // insert a new Restaurant
- () => frappe.tests.setup_doctype('Customer', customer),
- () => {
- return frappe.tests.make('Restaurant', [
- // values to be set
- {__newname: 'Test Restaurant 1'},
- {company: 'Test Company'},
- {invoice_series_prefix: 'Test-Rest-1-Inv-'},
- {default_customer: 'Test Customer 1'}
- ])
- },
- () => frappe.timeout(3),
- () => {
- assert.equal(cur_frm.doc.company, 'Test Company');
- },
- () => {
- return frappe.tests.make('Restaurant', [
- // values to be set
- {__newname: 'Test Restaurant 2'},
- {company: 'Test Company'},
- {invoice_series_prefix: 'Test-Rest-3-Inv-'},
- {default_customer: 'Test Customer 2'}
- ]);
- },
- () => frappe.timeout(3),
- () => {
- assert.equal(cur_frm.doc.company, 'Test Company');
- },
- () => done()
- ]);
-});
diff --git a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js b/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js
deleted file mode 100644
index f5ab9f0..0000000
--- a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Restaurant Menu", function (assert) {
- let done = assert.async();
-
- let items = {
- "Food Item 1": [
- {item_code: "Food Item 1"},
- {item_group: "Products"},
- {is_stock_item: 1},
- ],
- "Food Item 2": [
- {item_code: "Food Item 2"},
- {item_group: "Products"},
- {is_stock_item: 1},
- ],
- "Food Item 3": [
- {item_code: "Food Item 3"},
- {item_group: "Products"},
- {is_stock_item: 1},
- ]
- };
-
-
- // number of asserts
- assert.expect(0);
-
- frappe.run_serially([
- // insert a new Restaurant Menu
- () => frappe.tests.setup_doctype('Item', items),
- () => {
- return frappe.tests.make("Restaurant Menu", [
- {__newname: 'Restaurant Menu 1'},
- {restaurant: "Test Restaurant 1"},
- {items: [
- [
- {"item": "Food Item 1"},
- {"rate": 100}
- ],
- [
- {"item": "Food Item 2"},
- {"rate": 90}
- ],
- [
- {"item": "Food Item 3"},
- {"rate": 80}
- ]
- ]}
- ]);
- },
- () => frappe.timeout(2),
- () => {
- return frappe.tests.make("Restaurant Menu", [
- {__newname: 'Restaurant Menu 2'},
- {restaurant: "Test Restaurant 2"},
- {items: [
- [
- {"item": "Food Item 1"},
- {"rate": 105}
- ],
- [
- {"item": "Food Item 3"},
- {"rate": 85}
- ]
- ]}
- ]);
- },
- () => frappe.timeout(2),
- () => frappe.set_route('Form', 'Restaurant', 'Test Restaurant 1'),
- () => cur_frm.set_value('active_menu', 'Restaurant Menu 1'),
- () => cur_frm.save(),
- () => done()
- ]);
-
-});
diff --git a/erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js b/erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js
deleted file mode 100644
index fec2a21..0000000
--- a/erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Restaurant Order Entry", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(5);
-
- frappe.run_serially([
- // insert a new Restaurant Order Entry
- () => frappe.set_route('Form', 'Restaurant Settings'),
- () => cur_frm.set_value('default_customer', 'Test Customer 1'),
- () => cur_frm.save(),
- () => frappe.set_route('Form', 'Restaurant Order Entry'),
- () => frappe.click_button('Clear'),
- () => frappe.timeout(2),
- () => cur_frm.set_value('restaurant_table', 'Test-Restaurant-1-01'),
- () => cur_frm.set_value('add_item', 'Food Item 1'),
- () => frappe.timeout(0.5),
- () => {
- var e = $.Event( "keyup", {which: 13} );
- $('input[data-fieldname="add_item"]').trigger(e);
- return frappe.timeout(0.5);
- },
- () => cur_frm.set_value('add_item', 'Food Item 1'),
- () => {
- var e = $.Event( "keyup", {which: 13} );
- $('input[data-fieldname="add_item"]').trigger(e);
- return frappe.timeout(0.5);
- },
- () => cur_frm.set_value('add_item', 'Food Item 2'),
- () => {
- var e = $.Event( "keyup", {which: 13} );
- $('input[data-fieldname="add_item"]').trigger(e);
- return frappe.timeout(0.5);
- },
- () => {
- assert.equal(cur_frm.doc.items[0].item, 'Food Item 1');
- assert.equal(cur_frm.doc.items[0].qty, 2);
- assert.equal(cur_frm.doc.items[1].item, 'Food Item 2');
- assert.equal(cur_frm.doc.items[1].qty, 1);
- },
- () => frappe.click_button('Update'),
- () => frappe.timeout(2),
- () => {
- assert.equal(cur_frm.doc.grand_total, 290);
- }
- () => done()
- ]);
-
-});
diff --git a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js b/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js
deleted file mode 100644
index eeea5a9..0000000
--- a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Restaurant Reservation", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Restaurant Reservation
- () => frappe.tests.make('Restaurant Reservation', [
- // values to be set
- {restaurant: 'Gokul - JP Nagar'},
- {customer_name: 'test customer'},
- {reservation_time: frappe.datetime.now_date() + " 19:00:00"},
- {no_of_people: 4},
- ]),
- () => {
- assert.equal(cur_frm.doc.reservation_end_time,
- frappe.datetime.now_date() + ' 20:00:00');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js b/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js
deleted file mode 100644
index 16035f0..0000000
--- a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Restaurant Table", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(0);
-
- frappe.run_serially([
- // insert a new Restaurant Table
- () => frappe.tests.make('Restaurant Table', [
- // values to be set
- {restaurant: 'Test Restaurant 1'},
- {no_of_seats: 4},
- ]),
- () => frappe.tests.make('Restaurant Table', [
- // values to be set
- {restaurant: 'Test Restaurant 1'},
- {no_of_seats: 5},
- ]),
- () => frappe.tests.make('Restaurant Table', [
- // values to be set
- {restaurant: 'Test Restaurant 1'},
- {no_of_seats: 2},
- ]),
- () => frappe.tests.make('Restaurant Table', [
- // values to be set
- {restaurant: 'Test Restaurant 1'},
- {no_of_seats: 2},
- ]),
- () => frappe.tests.make('Restaurant Table', [
- // values to be set
- {restaurant: 'Test Restaurant 1'},
- {no_of_seats: 6},
- ]),
- () => done()
- ]);
-
-});
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 0c8c53a..b7f74df 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -196,20 +196,19 @@
if not lead.lead_name:
frappe.throw(_("Please mention the Lead Name in Lead {0}").format(self.lead_name))
- if lead.company_name:
- contact_names = frappe.get_all('Dynamic Link', filters={
- "parenttype":"Contact",
- "link_doctype":"Lead",
- "link_name":self.lead_name
- }, fields=["parent as name"])
+ contact_names = frappe.get_all('Dynamic Link', filters={
+ "parenttype":"Contact",
+ "link_doctype":"Lead",
+ "link_name":self.lead_name
+ }, fields=["parent as name"])
- for contact_name in contact_names:
- contact = frappe.get_doc('Contact', contact_name.get('name'))
- if not contact.has_link('Customer', self.name):
- contact.append('links', dict(link_doctype='Customer', link_name=self.name))
- contact.save(ignore_permissions=self.flags.ignore_permissions)
+ for contact_name in contact_names:
+ contact = frappe.get_doc('Contact', contact_name.get('name'))
+ if not contact.has_link('Customer', self.name):
+ contact.append('links', dict(link_doctype='Customer', link_name=self.name))
+ contact.save(ignore_permissions=self.flags.ignore_permissions)
- else:
+ if not contact_names:
lead.lead_name = lead.lead_name.lstrip().split(" ")
lead.first_name = lead.lead_name[0]
lead.last_name = " ".join(lead.lead_name[1:])
diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.js b/erpnext/selling/doctype/product_bundle/test_product_bundle.js
deleted file mode 100644
index 0dc90ec..0000000
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.test("test sales order", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Product Bundle', [
- {new_item_code: 'Computer'},
- {items: [
- [
- {item_code:'CPU'},
- {qty:1}
- ],
- [
- {item_code:'Screen'},
- {qty:1}
- ],
- [
- {item_code:'Keyboard'},
- {qty:1}
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_code=='CPU', "Item Code correct");
- assert.ok(cur_frm.doc.items[1].item_code=='Screen', "Item Code correct");
- assert.ok(cur_frm.doc.items[2].item_code=='Keyboard', "Item Code correct");
- assert.ok(cur_frm.doc.new_item_code == "Computer", "Parent Item correct");
- },
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation.js b/erpnext/selling/doctype/quotation/tests/test_quotation.js
deleted file mode 100644
index ad942fe..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation.js
+++ /dev/null
@@ -1,58 +0,0 @@
-QUnit.test("test: quotation", function (assert) {
- assert.expect(12);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Quotation", [
- {customer: "Test Customer 1"},
- {items: [
- [
- {"item_code": "Test Product 1"},
- {"qty": 5}
- ]]
- },
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
-
- // calculate_taxes_and_totals
- assert.ok(cur_frm.doc.grand_total === 500, String(cur_frm.doc.grand_total));
- },
- () => cur_frm.set_value("customer_address", "Test1-Billing"),
- () => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
- () => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"),
- () => cur_frm.set_value("currency", "USD"),
- () => frappe.timeout(0.3),
- () => cur_frm.set_value("selling_price_list", "Test-Selling-USD"),
- () => frappe.timeout(0.5),
- () => cur_frm.doc.items[0].rate = 200,
- () => frappe.timeout(0.3),
- () => cur_frm.set_value("tc_name", "Test Term 1"),
- () => cur_frm.set_value("payment_schedule", []),
- () => frappe.timeout(0.5),
- () => cur_frm.save(),
- () => {
- // Check Address and Contact Info
- assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed");
- assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed");
- assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed");
-
- // Check Currency
- assert.ok(cur_frm.doc.currency == "USD", "Currency Changed");
- assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed");
- assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
- assert.equal(cur_frm.doc.total, 1000, "New Total Calculated");
-
- // Check Terms and Conditions
- assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
-
- assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
- assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
-
- },
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js b/erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js
deleted file mode 100644
index 26a099e..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js
+++ /dev/null
@@ -1,41 +0,0 @@
-QUnit.module('Quotation');
-
-QUnit.test("test quotation submit cancel amend", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 1'}
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get uom details
- assert.ok(cur_frm.doc.grand_total== 500, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
- () => frappe.tests.click_button('Close'),
- () => frappe.tests.click_button('Cancel'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.5),
- () => frappe.tests.click_button('Amend'),
- () => cur_frm.save(),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js
deleted file mode 100644
index b59bb05..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js
+++ /dev/null
@@ -1,43 +0,0 @@
-QUnit.module('Quotation');
-
-QUnit.test("test quotation with additional discount in grand total", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {apply_discount_on:'Grand Total'},
- {additional_discount_percentage:10},
- {payment_schedule: []}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js
deleted file mode 100644
index f5172fb..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js
+++ /dev/null
@@ -1,37 +0,0 @@
-QUnit.module('Quotation');
-
-QUnit.test("test quotation with item wise discount", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- {'discount_percentage': 10},
- {'margin_type': 'Percentage'}
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js
deleted file mode 100644
index 0d34099..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.module('Selling');
-
-QUnit.test("test quotation with margin", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {selling_price_list: 'Test-Selling-USD'},
- {currency: 'USD'},
- {items: [
- [
- {'item_code': 'Test Product 4'},
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 1},
- {'margin_type': 'Percentage'},
- {'margin_rate_or_amount': 20}
- ]
- ]}
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.items[0].rate_with_margin == 240, "Margin rate correct");
- assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 240, "Base margin rate correct");
- assert.ok(cur_frm.doc.total == 240, "Amount correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js
deleted file mode 100644
index 84be56f..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js
+++ /dev/null
@@ -1,38 +0,0 @@
-QUnit.module('Quotation');
-
-QUnit.test("test quotation with multi uom", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- {'uom': 'unit'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get uom details
- assert.ok(cur_frm.doc.items[0].uom=='Unit', "Multi Uom correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 5000, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js
deleted file mode 100644
index 17c5dd2..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.module('Quotation');
-
-QUnit.test("test quotation with shipping rule", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {shipping_rule:'Next Day Shipping'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 550, "Grand total correct ");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js
deleted file mode 100644
index 5e21f81..0000000
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js
+++ /dev/null
@@ -1,40 +0,0 @@
-QUnit.module('Quotation');
-
-QUnit.test("test quotation with taxes and charges", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Quotation', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order.js
deleted file mode 100644
index c99f9ef..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order.js
+++ /dev/null
@@ -1,68 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order", function(assert) {
- assert.expect(12);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5.123},
- {'item_code': 'Test Product 3'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {selling_price_list:'Test-Selling-USD'},
- {currency: 'USD'}
- ]);
- },
- () => frappe.timeout(1.5),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 3', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- },
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => cur_frm.print_doc(),
- () => frappe.timeout(1),
- () => {
- // Payment Terms
- assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
- assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
-
- // totals
- assert.ok(cur_frm.doc.items[0].price_list_rate==250, "Item 1 price_list_rate");
- assert.ok(cur_frm.doc.net_total== 1280.75, "net total correct ");
- assert.ok(cur_frm.doc.base_grand_total== flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')), String(flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')) + ' ' + cur_frm.doc.base_grand_total));
- assert.ok(cur_frm.doc.grand_total== 1511.29 , "grand total correct ");
- assert.ok(cur_frm.doc.rounded_total== 1511.30, "rounded total correct ");
-
- // print format
- assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
- frappe.timeout(1);
- assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected");
- },
- () => cur_frm.print_doc(),
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js
deleted file mode 100644
index 79d798b..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js
+++ /dev/null
@@ -1,58 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) {
-//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.new_doc('Customer'),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("customer_name", "Test Customer 10"),
- () => cur_frm.add_child('credit_limits', {
- 'company': cur_frm.doc.company || '_Test Company'
- 'credit_limit': 1000,
- 'bypass_credit_limit_check': 1}),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
-
- () => frappe.new_doc('Item'),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("item_code", "Test Product 10"),
- () => cur_frm.set_value("item_group", "Products"),
- () => cur_frm.set_value("standard_rate", 100),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
-
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 5'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 10'},
- ]
- ]}
-
- ]);
- },
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => assert.equal("Confirm", cur_dialog.title,'confirmation for submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(3),
- () => {
-
- assert.ok(cur_frm.doc.status=="To Deliver and Bill", "It is submited. Credit limit is NOT checked for sales order");
-
-
- },
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js
deleted file mode 100644
index de61a61..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js
+++ /dev/null
@@ -1,43 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order with additional discount in grand total", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => {
- return frappe.tests.set_form_values(cur_frm, [
- {apply_discount_on:'Grand Total'},
- {additional_discount_percentage:10},
- {payment_schedule: []}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js
deleted file mode 100644
index 2c48108..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js
+++ /dev/null
@@ -1,38 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- {'discount_percentage': 10},
- {'margin_type': 'Percentage'}
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {payment_terms_template: '_Test Payment Term Template UI'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js
deleted file mode 100644
index 9eebfda..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js
+++ /dev/null
@@ -1,37 +0,0 @@
-QUnit.module('Selling');
-
-QUnit.test("test sales order with margin", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer:'Test Customer 1'},
- {selling_price_list: 'Test-Selling-USD'},
- {currency: 'USD'},
- {items: [
- [
- {'item_code': 'Test Product 4'},
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 1},
- {'margin_type': 'Amount'},
- {'margin_rate_or_amount': 20}
- ]
- ]},
- ]);
- },
-
- () => cur_frm.save(),
- () => {
- // get_rate_details
- assert.ok(cur_frm.doc.items[0].rate_with_margin == 220, "Margin rate correct");
- assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 220, "Base margin rate correct");
- assert.ok(cur_frm.doc.total == 220, "Amount correct");
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js
deleted file mode 100644
index 84301f5..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js
+++ /dev/null
@@ -1,38 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- {'uom': 'Unit'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get uom details
- assert.ok(cur_frm.doc.items[0].uom=='Unit', "Multi Uom correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 5000, "Grand total correct ");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js
deleted file mode 100644
index be76c49..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Order", function (assert) {
- assert.expect(2);
- let done = assert.async();
- let delivery_date = frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1);
-
- frappe.run_serially([
- // insert a new Sales Order
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: "Test Customer 1"},
- {delivery_date: delivery_date},
- {order_type: 'Sales'},
- {items: [
- [
- {"item_code": "Test Product 1"},
- {"qty": 5},
- {'rate': 100},
- ]]
- }
- ])
- },
- () => {
- assert.ok(cur_frm.doc.items[0].delivery_date == delivery_date);
- },
- () => frappe.timeout(1),
- // make SO without delivery date in parent,
- // parent delivery date should be set based on final delivery date entered in item
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: "Test Customer 1"},
- {order_type: 'Sales'},
- {items: [
- [
- {"item_code": "Test Product 1"},
- {"qty": 5},
- {'rate': 100},
- {'delivery_date': delivery_date}
- ],
- [
- {"item_code": "Test Product 2"},
- {"qty": 5},
- {'rate': 100},
- {'delivery_date': frappe.datetime.add_days(delivery_date, 5)}
- ]]
- }
- ])
- },
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => {
- assert.ok(cur_frm.doc.delivery_date == frappe.datetime.add_days(delivery_date, 5));
- },
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js
deleted file mode 100644
index e91fb01..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js
+++ /dev/null
@@ -1,34 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order with shipping rule", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 3'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 2'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 2', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 675, "Grand total correct ");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js
deleted file mode 100644
index 7d1211f..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order with shipping rule", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {shipping_rule:'Next Day Shipping'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get grand_total details
- assert.ok(cur_frm.doc.grand_total== 550, "Grand total correct ");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js
deleted file mode 100644
index a3668ab..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js
+++ /dev/null
@@ -1,40 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test sales order with taxes and charges", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 1'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 4'},
- ]
- ]},
- {customer_address: 'Test1-Billing'},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js
deleted file mode 100644
index 8de39f9..0000000
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js
+++ /dev/null
@@ -1,62 +0,0 @@
-QUnit.module('Sales Order');
-
-QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert) {
-//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.new_doc('Customer'),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("customer_name", "Test Customer 11"),
- () => cur_frm.add_child('credit_limits', {
- 'credit_limit': 1000,
- 'company': '_Test Company',
- 'bypass_credit_limit_check': 1}),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
-
- () => frappe.new_doc('Item'),
- () => frappe.timeout(1),
- () => frappe.click_link('Edit in full page'),
- () => cur_frm.set_value("item_code", "Test Product 11"),
- () => cur_frm.set_value("item_group", "Products"),
- () => cur_frm.set_value("standard_rate", 100),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
-
- () => {
- return frappe.tests.make('Sales Order', [
- {customer: 'Test Customer 11'},
- {items: [
- [
- {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
- {'qty': 5},
- {'item_code': 'Test Product 11'},
- ]
- ]}
-
- ]);
- },
- () => cur_frm.save(),
- () => frappe.tests.click_button('Submit'),
- () => assert.equal("Confirm", cur_dialog.title,'confirmation for submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(3),
- () => {
-
- if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/))
- {
- /*Match found */
- assert.ok(true, "Credit Limit crossed message received");
- }
-
-
- },
- () => cur_dialog.cancel(),
- () => done()
- ]);
-});
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index 82e5d0c..0c0acc7 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -61,6 +61,7 @@
IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay,
soi.qty, soi.delivered_qty,
(soi.qty - soi.delivered_qty) AS pending_qty,
+ IF((SELECT pending_qty) = 0, (TO_SECONDS(Max(dn.posting_date))-TO_SECONDS(so.transaction_date)), 0) as time_taken_to_deliver,
IFNULL(SUM(sii.qty), 0) as billed_qty,
soi.base_amount as amount,
(soi.delivered_qty * soi.base_rate) as delivered_qty_amount,
@@ -70,9 +71,13 @@
so.company, soi.name
FROM
`tabSales Order` so,
- `tabSales Order Item` soi
+ (`tabSales Order Item` soi
LEFT JOIN `tabSales Invoice Item` sii
- ON sii.so_detail = soi.name and sii.docstatus = 1
+ ON sii.so_detail = soi.name and sii.docstatus = 1)
+ LEFT JOIN `tabDelivery Note Item` dni
+ on dni.so_detail = soi.name
+ RIGHT JOIN `tabDelivery Note` dn
+ on dni.parent = dn.name and dn.docstatus = 1
WHERE
soi.parent = so.name
and so.status not in ('Stopped', 'Closed', 'On Hold')
@@ -259,6 +264,12 @@
"fieldname": "delay",
"fieldtype": "Data",
"width": 100
+ },
+ {
+ "label": _("Time Taken to Deliver"),
+ "fieldname": "time_taken_to_deliver",
+ "fieldtype": "Duration",
+ "width": 100
}
])
if not filters.get("group_by_so"):
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index e2e0db4..540aca2 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -41,6 +41,7 @@
me.frm.set_query('contact_person', erpnext.queries.contact_query);
me.frm.set_query('customer_address', erpnext.queries.address_query);
me.frm.set_query('shipping_address_name', erpnext.queries.address_query);
+ me.frm.set_query('dispatch_address_name', erpnext.queries.dispatch_address_query);
if(this.frm.fields_dict.selling_price_list) {
diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json
index 9e55702..89be607 100644
--- a/erpnext/setup/doctype/company/test_records.json
+++ b/erpnext/setup/doctype/company/test_records.json
@@ -36,7 +36,7 @@
"abbr": "_TC3",
"company_name": "_Test Company 3",
"is_group": 1,
- "country": "India",
+ "country": "Pakistan",
"default_currency": "INR",
"doctype": "Company",
"domain": "Manufacturing",
@@ -49,7 +49,7 @@
"company_name": "_Test Company 4",
"parent_company": "_Test Company 3",
"is_group": 1,
- "country": "India",
+ "country": "Pakistan",
"default_currency": "INR",
"doctype": "Company",
"domain": "Manufacturing",
@@ -61,7 +61,7 @@
"abbr": "_TC5",
"company_name": "_Test Company 5",
"parent_company": "_Test Company 4",
- "country": "India",
+ "country": "Pakistan",
"default_currency": "INR",
"doctype": "Company",
"domain": "Manufacturing",
diff --git a/erpnext/setup/doctype/company/tests/test_company.js b/erpnext/setup/doctype/company/tests/test_company.js
deleted file mode 100644
index b568494..0000000
--- a/erpnext/setup/doctype/company/tests/test_company.js
+++ /dev/null
@@ -1,25 +0,0 @@
-QUnit.module('setup');
-
-QUnit.test("Test: Company [SetUp]", function (assert) {
- assert.expect(2);
- let done = assert.async();
-
- frappe.run_serially([
- // test company creation
- () => frappe.set_route("List", "Company", "List"),
- () => frappe.new_doc("Company"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("company_name", "Test Company"),
- () => cur_frm.set_value("abbr", "TC"),
- () => cur_frm.set_value("domain", "Services"),
- () => cur_frm.set_value("default_currency", "INR"),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Debtors - TC", cur_frm.doc.default_receivable_account,
- 'chart of acounts created'),
- () => assert.equal("Main - TC", cur_frm.doc.cost_center,
- 'chart of cost centers created'),
- () => done()
- ]);
-});
diff --git a/erpnext/setup/doctype/company/tests/test_company_production.js b/erpnext/setup/doctype/company/tests/test_company_production.js
deleted file mode 100644
index a4c1e2e..0000000
--- a/erpnext/setup/doctype/company/tests/test_company_production.js
+++ /dev/null
@@ -1,19 +0,0 @@
-QUnit.test("Test: Company", function (assert) {
- assert.expect(0);
-
- let done = assert.async();
-
- frappe.run_serially([
- // Added company for Work Order testing
- () => frappe.set_route("List", "Company"),
- () => frappe.new_doc("Company"),
- () => frappe.timeout(1),
- () => cur_frm.set_value("company_name", "For Testing"),
- () => cur_frm.set_value("abbr", "RB"),
- () => cur_frm.set_value("default_currency", "INR"),
- () => cur_frm.save(),
- () => frappe.timeout(1),
-
- () => done()
- ]);
-});
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 14b7951..91e8eff 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -1178,11 +1178,13 @@
{
"title": "Reverse Charge In-State",
"is_inter_state": 0,
+ "is_reverse_charge": 1,
"gst_state": ""
},
{
"title": "Reverse Charge Out-State",
"is_inter_state": 1,
+ "is_reverse_charge": 1,
"gst_state": ""
},
{
diff --git a/erpnext/stock/doctype/batch/test_batch.js b/erpnext/stock/doctype/batch/test_batch.js
deleted file mode 100644
index 2d2150b..0000000
--- a/erpnext/stock/doctype/batch/test_batch.js
+++ /dev/null
@@ -1,22 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test Batch", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Batch', [
- {batch_id:'TEST-BATCH-001'},
- {item:'Test Product 4'},
- {expiry_date:frappe.datetime.add_days(frappe.datetime.now_date(), 2)},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.batch_id=='TEST-BATCH-001', "Batch Id correct");
- },
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 37b5411..0ef7ce2 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -130,8 +130,8 @@
"""WARNING: This function is deprecated. Inline this function instead of using it."""
from erpnext.stock.stock_ledger import repost_current_voucher
- update_qty(bin_name, args)
repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher)
+ update_qty(bin_name, args)
def get_bin_details(bin_name):
return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty',
@@ -139,13 +139,23 @@
'reserved_qty_for_sub_contract'], as_dict=1)
def update_qty(bin_name, args):
- bin_details = get_bin_details(bin_name)
+ from erpnext.controllers.stock_controller import future_sle_exists
- # update the stock values (for current quantities)
- if args.get("voucher_type")=="Stock Reconciliation":
- actual_qty = args.get('qty_after_transaction')
- else:
- actual_qty = bin_details.actual_qty + flt(args.get("actual_qty"))
+ bin_details = get_bin_details(bin_name)
+ # actual qty is already updated by processing current voucher
+ actual_qty = bin_details.actual_qty
+
+ # actual qty is not up to date in case of backdated transaction
+ if future_sle_exists(args):
+ actual_qty = frappe.db.get_value("Stock Ledger Entry",
+ filters={
+ "item_code": args.get("item_code"),
+ "warehouse": args.get("warehouse"),
+ "is_cancelled": 0
+ },
+ fieldname="qty_after_transaction",
+ order_by="posting_date desc, posting_time desc, creation desc",
+ ) or 0.0
ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty"))
reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty"))
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.js b/erpnext/stock/doctype/delivery_note/test_delivery_note.js
deleted file mode 100644
index 76f7989..0000000
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.js
+++ /dev/null
@@ -1,35 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test delivery note", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Delivery Note', [
- {customer:'Test Customer 1'},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 5},
- ]
- ]},
- {shipping_address_name: 'Test1-Shipping'},
- {contact_person: 'Contact 1-Test Customer 1'},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {transporter_name:'TEST TRANSPORT'},
- {lr_no:'MH-04-FG 1111'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.grand_total==590, " Grand Total correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js b/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js
deleted file mode 100644
index 9f1375f..0000000
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js
+++ /dev/null
@@ -1,36 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test delivery note with margin", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Delivery Note', [
- {customer:'Test Customer 1'},
- {selling_price_list: 'Test-Selling-USD'},
- {currency: 'USD'},
- {items: [
- [
- {'item_code': 'Test Product 4'},
- {'qty': 1},
- {'margin_type': 'Amount'},
- {'margin_rate_or_amount': 10}
- ]
- ]},
- ]);
- },
-
- () => cur_frm.save(),
- () => {
- // get_rate_details
- assert.ok(cur_frm.doc.items[0].rate_with_margin == 210, "Margin rate correct");
- assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 210, "Base margin rate correct");
- assert.ok(cur_frm.doc.total == 210, "Amount correct");
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/item/tests/test_item.js b/erpnext/stock/doctype/item/tests/test_item.js
deleted file mode 100644
index 7f7e72d..0000000
--- a/erpnext/stock/doctype/item/tests/test_item.js
+++ /dev/null
@@ -1,121 +0,0 @@
-QUnit.module('stock');
-QUnit.test("test: item", function (assert) {
- assert.expect(6);
- let done = assert.async();
- let keyboard_cost = 800;
- let screen_cost = 4000;
- let CPU_cost = 15000;
- let scrap_cost = 100;
- let no_of_items_to_stock = 100;
- let is_stock_item = 1;
- frappe.run_serially([
- // test item creation
- () => frappe.set_route("List", "Item"),
-
- // Create a keyboard item
- () => frappe.tests.make(
- "Item", [
- {item_code: "Keyboard"},
- {item_group: "Products"},
- {is_stock_item: is_stock_item},
- {standard_rate: keyboard_cost},
- {opening_stock: no_of_items_to_stock},
- {default_warehouse: "Stores - FT"}
- ]
- ),
- () => {
- assert.ok(cur_frm.doc.item_name.includes('Keyboard'),
- 'Item Keyboard created correctly');
- assert.ok(cur_frm.doc.item_code.includes('Keyboard'),
- 'item_code for Keyboard set correctly');
- assert.ok(cur_frm.doc.item_group.includes('Products'),
- 'item_group for Keyboard set correctly');
- assert.equal(cur_frm.doc.is_stock_item, is_stock_item,
- 'is_stock_item for Keyboard set correctly');
- assert.equal(cur_frm.doc.standard_rate, keyboard_cost,
- 'standard_rate for Keyboard set correctly');
- assert.equal(cur_frm.doc.opening_stock, no_of_items_to_stock,
- 'opening_stock for Keyboard set correctly');
- },
-
- // Create a Screen item
- () => frappe.tests.make(
- "Item", [
- {item_code: "Screen"},
- {item_group: "Products"},
- {is_stock_item: is_stock_item},
- {standard_rate: screen_cost},
- {opening_stock: no_of_items_to_stock},
- {default_warehouse: "Stores - FT"}
- ]
- ),
-
- // Create a CPU item
- () => frappe.tests.make(
- "Item", [
- {item_code: "CPU"},
- {item_group: "Products"},
- {is_stock_item: is_stock_item},
- {standard_rate: CPU_cost},
- {opening_stock: no_of_items_to_stock},
- {default_warehouse: "Stores - FT"}
- ]
- ),
-
- // Create a laptop item
- () => frappe.tests.make(
- "Item", [
- {item_code: "Laptop"},
- {item_group: "Products"},
- {default_warehouse: "Stores - FT"}
- ]
- ),
- () => frappe.tests.make(
- "Item", [
- {item_code: "Computer"},
- {item_group: "Products"},
- {is_stock_item: 0},
- ]
- ),
-
- // Create a scrap item
- () => frappe.tests.make(
- "Item", [
- {item_code: "Scrap item"},
- {item_group: "Products"},
- {is_stock_item: is_stock_item},
- {standard_rate: scrap_cost},
- {opening_stock: no_of_items_to_stock},
- {default_warehouse: "Stores - FT"}
- ]
- ),
- () => frappe.tests.make(
- "Item", [
- {item_code: "Test Product 4"},
- {item_group: "Products"},
- {is_stock_item: 1},
- {has_batch_no: 1},
- {create_new_batch: 1},
- {uoms:
- [
- [
- {uom:"Unit"},
- {conversion_factor: 10},
- ]
- ]
- },
- {taxes:
- [
- [
- {tax_type:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company"))},
- {tax_rate: 0},
- ]
- ]},
- {has_serial_no: 1},
- {standard_rate: 100},
- {opening_stock: 100},
- ]
- ),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/item_price/test_item_price.js b/erpnext/stock/doctype/item_price/test_item_price.js
deleted file mode 100644
index 49dbaa2..0000000
--- a/erpnext/stock/doctype/item_price/test_item_price.js
+++ /dev/null
@@ -1,22 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test item price", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Item Price', [
- {price_list:'Test-Selling-USD'},
- {item_code: 'Test Product 4'},
- {price_list_rate: 200}
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.item_name == 'Test Product 4', "Item name correct");
- assert.ok(cur_frm.doc.price_list_rate == 200, "Price list rate correct");
- },
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request.js b/erpnext/stock/doctype/material_request/tests/test_material_request.js
deleted file mode 100644
index a2cd03b..0000000
--- a/erpnext/stock/doctype/material_request/tests/test_material_request.js
+++ /dev/null
@@ -1,39 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request", function(assert) {
- assert.expect(5);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Material Request', [
- {items: [
- [
- {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)},
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ],
- [
- {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 6)},
- {'qty': 2},
- {'item_code': 'Test Product 2'},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 5), "Schedule Date correct");
-
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 5), "Schedule Date correct");
-
- assert.ok(cur_frm.doc.items[1].item_name=='Test Product 2', "Item name correct");
- assert.ok(cur_frm.doc.items[1].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 6), "Schedule Date correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js
deleted file mode 100644
index 6fb55ae..0000000
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js
+++ /dev/null
@@ -1,27 +0,0 @@
-QUnit.module('manufacturing');
-
-QUnit.test("test material request get items from BOM", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('Form', 'BOM'),
- () => frappe.timeout(3),
- () => frappe.click_button('Get Items from BOM'),
- () => frappe.timeout(3),
- () => {
- assert.ok(cur_dialog, 'dialog appeared');
- },
- () => cur_dialog.set_value('bom', 'Laptop'),
- () => cur_dialog.set_value('warehouse', 'Laptop Scrap Warehouse'),
- () => frappe.click_button('Get Items from BOM'),
- () => frappe.timeout(3),
- () => {
- assert.ok(cur_frm.doc.items[0].item_code, "First row is not empty");
- assert.ok(cur_frm.doc.items[0].item_name, "Item name is not empty");
- assert.equal(cur_frm.doc.items[0].item_name, "Laptop", cur_frm.doc.items[0].item_name);
- },
- () => cur_frm.doc.items[0].schedule_date = '2017-12-12',
- () => cur_frm.save(),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js
deleted file mode 100644
index 137079b..0000000
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js
+++ /dev/null
@@ -1,29 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Material Request', [
- {material_request_type:'Manufacture'},
- {items: [
- [
- {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)},
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js
deleted file mode 100644
index b03a854..0000000
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js
+++ /dev/null
@@ -1,29 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request for issue", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Material Request', [
- {material_request_type:'Material Issue'},
- {items: [
- [
- {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)},
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js
deleted file mode 100644
index 7c62c2e..0000000
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js
+++ /dev/null
@@ -1,29 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request for transfer", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Material Request', [
- {material_request_type:'Manufacture'},
- {items: [
- [
- {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)},
- {'qty': 5},
- {'item_code': 'Test Product 1'},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/price_list/test_price_list_uom.js b/erpnext/stock/doctype/price_list/test_price_list_uom.js
deleted file mode 100644
index 3896c0e..0000000
--- a/erpnext/stock/doctype/price_list/test_price_list_uom.js
+++ /dev/null
@@ -1,58 +0,0 @@
-QUnit.module('Price List');
-
-QUnit.test("test price list with uom dependancy", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
-
- () => frappe.set_route('Form', 'Price List', 'Standard Buying'),
- () => {
- cur_frm.set_value('price_not_uom_dependent','1');
- frappe.timeout(1);
- },
- () => cur_frm.save(),
-
- () => frappe.timeout(1),
-
- () => {
- return frappe.tests.make('Item Price', [
- {price_list:'Standard Buying'},
- {item_code: 'Test Product 3'},
- {price_list_rate: 200}
- ]);
- },
-
- () => cur_frm.save(),
-
- () => {
- return frappe.tests.make('Purchase Order', [
- {supplier: 'Test Supplier'},
- {currency: 'INR'},
- {buying_price_list: 'Standard Buying'},
- {items: [
- [
- {"item_code": 'Test Product 3'},
- {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)},
- {"uom": 'Nos'},
- {"conversion_factor": 3}
- ]
- ]},
-
- ]);
- },
-
- () => cur_frm.save(),
- () => frappe.timeout(0.3),
-
- () => {
- assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 3', "Item code correct");
- assert.ok(cur_frm.doc.items[0].price_list_rate == 200, "Price list rate correct");
- },
-
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(1),
-
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js
deleted file mode 100644
index d1f4485..0000000
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js
+++ /dev/null
@@ -1,42 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test Purchase Receipt", function(assert) {
- assert.expect(4);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Purchase Receipt', [
- {supplier: 'Test Supplier'},
- {items: [
- [
- {'received_qty': 5},
- {'qty': 4},
- {'item_code': 'Test Product 1'},
- {'uom': 'Nos'},
- {'warehouse':'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {'rejected_warehouse':'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- ]
- ]},
- {taxes_and_charges: 'TEST In State GST - FT'},
- {tc_name: 'Test Term 1'},
- {terms: 'This is Test'}
- ]);
- },
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- // get tax details
- assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
- // get tax account head details
- assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
- // grand_total Calculated
- assert.ok(cur_frm.doc.grand_total==472, "Grad Total correct");
-
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index b2ad07f..fb3b355 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -46,7 +46,7 @@
self.db_set('status', self.status)
def on_submit(self):
- if not frappe.flags.in_test or self.flags.dont_run_in_test:
+ if not frappe.flags.in_test or self.flags.dont_run_in_test or frappe.flags.dont_execute_stock_reposts:
return
frappe.enqueue(repost, timeout=1800, queue='long',
@@ -97,7 +97,8 @@
return
doc.set_status('In Progress')
- frappe.db.commit()
+ if not frappe.flags.in_test:
+ frappe.db.commit()
repost_sl_entries(doc)
repost_gl_entries(doc)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index a3d44af..6e1e0d4 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -1,7 +1,6 @@
{
"actions": [],
"allow_import": 1,
- "allow_rename": 1,
"autoname": "field:serial_no",
"creation": "2013-05-16 10:59:15",
"description": "Distinct unit of an Item",
@@ -434,10 +433,11 @@
"icon": "fa fa-barcode",
"idx": 1,
"links": [],
- "modified": "2021-01-08 14:31:15.375996",
+ "modified": "2021-12-23 10:44:30.299450",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -476,5 +476,6 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 38291d1..2947faf 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -194,23 +194,6 @@
if sle_exists:
frappe.throw(_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name))
- def before_rename(self, old, new, merge=False):
- if merge:
- frappe.throw(_("Sorry, Serial Nos cannot be merged"))
-
- def after_rename(self, old, new, merge=False):
- """rename serial_no text fields"""
- for dt in frappe.db.sql("""select parent from tabDocField
- where fieldname='serial_no' and fieldtype in ('Text', 'Small Text', 'Long Text')"""):
-
- for item in frappe.db.sql("""select name, serial_no from `tab%s`
- where serial_no like %s""" % (dt[0], frappe.db.escape('%' + old + '%'))):
-
- serial_nos = map(lambda i: new if i.upper()==old.upper() else i, item[1].split('\n'))
- frappe.db.sql("""update `tab%s` set serial_no = %s
- where name=%s""" % (dt[0], '%s', '%s'),
- ('\n'.join(list(serial_nos)), item[0]))
-
def update_serial_no_reference(self, serial_no=None):
last_sle = self.get_last_sle(serial_no)
self.set_purchase_details(last_sle.get("purchase_sle"))
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index 705b265..afe8218 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -39,9 +39,9 @@
"description": 'Test delivery note for shipment',
"qty": 5,
"uom": 'Nos',
- "warehouse": 'Stores - SC',
+ "warehouse": 'Stores - _TC',
"rate": item.standard_rate,
- "cost_center": 'Main - SC'
+ "cost_center": 'Main - _TC'
}
)
delivery_note.insert()
@@ -127,13 +127,7 @@
return create_shipment_address(address_title, company_name, 80331)
def get_shipment_company():
- company_name = 'Shipment Company'
- abbr = 'SC'
- companies = frappe.get_all("Company", fields=["name"], filters = {"company_name": company_name})
- if len(companies):
- return companies[0]
- else:
- return create_shipment_company(company_name, abbr)
+ return frappe.get_doc("Company", "_Test Company")
def get_shipment_item(company_name):
item_name = 'Testing Shipment item'
@@ -182,17 +176,6 @@
customer.insert()
return customer
-
-def create_shipment_company(company_name, abbr):
- company = frappe.new_doc("Company")
- company.company_name = company_name
- company.abbr = abbr
- company.default_currency = 'EUR'
- company.country = 'Germany'
- company.enable_perpetual_inventory = 0
- company.insert()
- return company
-
def create_shipment_customer(customer_name):
customer = frappe.new_doc("Customer")
customer.customer_name = customer_name
@@ -211,12 +194,12 @@
stock.posting_date = posting_date.strftime("%Y-%m-%d")
stock.append('items',
{
- "t_warehouse": 'Stores - SC',
+ "t_warehouse": 'Stores - _TC',
"item_code": item.name,
"qty": 5,
"uom": 'Nos',
"basic_rate": item.standard_rate,
- "cost_center": 'Main - SC'
+ "cost_center": 'Main - _TC'
}
)
stock.insert()
@@ -233,7 +216,7 @@
item.append('item_defaults',
{
"company": company_name,
- "default_warehouse": 'Stores - SC'
+ "default_warehouse": 'Stores - _TC'
}
)
item.insert()
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index a00d63e..93e303c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -35,10 +35,16 @@
from erpnext.stock.utils import get_bin, get_incoming_rate
-class IncorrectValuationRateError(frappe.ValidationError): pass
-class DuplicateEntryForWorkOrderError(frappe.ValidationError): pass
-class OperationsNotCompleteError(frappe.ValidationError): pass
-class MaxSampleAlreadyRetainedError(frappe.ValidationError): pass
+class FinishedGoodError(frappe.ValidationError):
+ pass
+class IncorrectValuationRateError(frappe.ValidationError):
+ pass
+class DuplicateEntryForWorkOrderError(frappe.ValidationError):
+ pass
+class OperationsNotCompleteError(frappe.ValidationError):
+ pass
+class MaxSampleAlreadyRetainedError(frappe.ValidationError):
+ pass
from erpnext.controllers.stock_controller import StockController
@@ -701,6 +707,11 @@
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
+
for d in self.items:
if d.t_warehouse and not d.s_warehouse:
if self.purpose=="Repack" or d.item_code == finished_item:
@@ -721,38 +732,64 @@
return finished_item
def validate_finished_goods(self):
- """validation: finished good quantity should be same as manufacturing quantity"""
- if not self.work_order: return
+ """
+ 1. Check if FG exists
+ 2. Check if Multiple FG Items are present
+ 3. Check FG Item and Qty against WO if present
+ """
+ production_item, wo_qty, finished_items = None, 0, []
- production_item, wo_qty = frappe.db.get_value("Work Order",
- self.work_order, ["production_item", "qty"])
+ wo_details = frappe.db.get_value(
+ "Work Order", self.work_order, ["production_item", "qty"]
+ )
+ if wo_details:
+ production_item, wo_qty = wo_details
- finished_items = []
for d in self.get('items'):
if d.is_finished_item:
+ if not self.work_order:
+ finished_items.append(d.item_code)
+ continue # Independent Manufacture Entry, no WO to match against
+
if d.item_code != production_item:
frappe.throw(_("Finished Item {0} does not match with Work Order {1}")
- .format(d.item_code, self.work_order))
+ .format(d.item_code, self.work_order)
+ )
elif flt(d.transfer_qty) > flt(self.fg_completed_qty):
- frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
- format(d.idx, d.transfer_qty, self.fg_completed_qty))
+ frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}")
+ .format(d.idx, d.transfer_qty, self.fg_completed_qty)
+ )
+
finished_items.append(d.item_code)
if len(set(finished_items)) > 1:
- frappe.throw(_("Multiple items cannot be marked as finished item"))
+ frappe.throw(
+ msg=_("Multiple items cannot be marked as finished item"),
+ title=_("Note"),
+ exc=FinishedGoodError
+ )
if self.purpose == "Manufacture":
if not finished_items:
- frappe.throw(_('Finished Good has not set in the stock entry {0}')
- .format(self.name))
+ frappe.throw(
+ msg=_("There must be atleast 1 Finished Good in this Stock Entry").format(self.name),
+ title=_("Missing Finished Good"),
+ exc=FinishedGoodError
+ )
- allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
- "overproduction_percentage_for_work_order"))
+ allowance_percentage = flt(
+ frappe.db.get_single_value(
+ "Manufacturing Settings","overproduction_percentage_for_work_order"
+ )
+ )
+ allowed_qty = wo_qty + ((allowance_percentage/100) * wo_qty)
- allowed_qty = wo_qty + (allowance_percentage/100 * wo_qty)
- if self.fg_completed_qty > allowed_qty:
- frappe.throw(_("For quantity {0} should not be greater than work order quantity {1}")
- .format(flt(self.fg_completed_qty), wo_qty))
+ # No work order could mean independent Manufacture entry, if so skip validation
+ if self.work_order and self.fg_completed_qty > allowed_qty:
+ frappe.throw(
+ _("For quantity {0} should not be greater than work order quantity {1}")
+ .format(flt(self.fg_completed_qty), wo_qty)
+ )
def update_stock_ledger(self):
sl_entries = []
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 5a9e77e..b874874 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -15,7 +15,10 @@
set_item_variant_settings,
)
from erpnext.stock.doctype.serial_no.serial_no import * # noqa
-from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse
+from erpnext.stock.doctype.stock_entry.stock_entry import (
+ FinishedGoodError,
+ move_sample_to_retention_warehouse,
+)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
@@ -929,6 +932,38 @@
distributed_costs = [d.additional_cost for d in se.items]
self.assertEqual([40.0, 60.0], distributed_costs)
+ def test_independent_manufacture_entry(self):
+ "Test FG items and incoming rate calculation in Maniufacture Entry without WO or BOM linked."
+ se = frappe.get_doc(
+ doctype="Stock Entry",
+ purpose="Manufacture",
+ stock_entry_type="Manufacture",
+ company="_Test Company",
+ items=[
+ frappe._dict(item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"),
+ frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC")
+ ]
+ )
+ # SE must have atleast one FG
+ self.assertRaises(FinishedGoodError, se.save)
+
+ se.items[0].is_finished_item = 1
+ se.items[1].is_finished_item = 1
+ # SE cannot have multiple FGs
+ self.assertRaises(FinishedGoodError, se.save)
+
+ se.items[0].is_finished_item = 0
+ se.save()
+
+ # Check if FG cost is calculated based on RM total cost
+ # RM total cost = 200, FG rate = 200/4(FG qty) = 50
+ self.assertEqual(se.items[1].basic_rate, 50)
+ self.assertEqual(se.value_difference, 0.0)
+ self.assertEqual(se.total_incoming_value, se.total_outgoing_value)
+
+ # teardown
+ se.delete()
+
@change_settings("Stock Settings", {"allow_negative_stock": 0})
def test_future_negative_sle(self):
# Initialize item, batch, warehouse, opening qty
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js
deleted file mode 100644
index e51c90c..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js
+++ /dev/null
@@ -1,26 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test manufacture from bom", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Stock Entry", [
- { purpose: "Manufacture" },
- { from_bom: 1 },
- { bom_no: "BOM-_Test Item - Non Whole UOM-001" },
- { fg_completed_qty: 2 }
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button("Update Rate and Availability"),
- () => {
- assert.ok(cur_frm.doc.items[1] === 0.75, " Finished Item Qty correct");
- assert.ok(cur_frm.doc.items[2] === 0.25, " Process Loss Item Qty correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
deleted file mode 100644
index a87a7fb..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
+++ /dev/null
@@ -1,30 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 5},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.total_outgoing_value==500, " Outgoing Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
deleted file mode 100644
index cae318d..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
+++ /dev/null
@@ -1,34 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material issue", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 4'},
- {'qty': 1},
- {'batch_no':'TEST-BATCH-001'},
- {'serial_no':'Test-Product-003'},
- {'basic_rate':100},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Close'),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- assert.ok(cur_frm.doc.total_outgoing_value==100, " Outgoing Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
deleted file mode 100644
index ef0286f..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
+++ /dev/null
@@ -1,31 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {purpose:'Material Receipt'},
- {to_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 5},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.total_incoming_value==500, " Incoming Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js
deleted file mode 100644
index 54e1ac8..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js
+++ /dev/null
@@ -1,34 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material receipt", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {purpose:'Material Receipt'},
- {to_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 4'},
- {'qty': 5},
- {'batch_no':'TEST-BATCH-001'},
- {'serial_no':'Test-Product-001\nTest-Product-002\nTest-Product-003\nTest-Product-004\nTest-Product-005'},
- {'basic_rate':100},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
- assert.ok(cur_frm.doc.total_incoming_value==500, " Incoming Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
deleted file mode 100644
index fac0b4b..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
+++ /dev/null
@@ -1,33 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material request", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {purpose:'Material Transfer'},
- {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {to_warehouse:'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 5},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.total_outgoing_value==500, " Outgoing Value correct");
- assert.ok(cur_frm.doc.total_incoming_value==500, " Incoming Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js
deleted file mode 100644
index 9f85307..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js
+++ /dev/null
@@ -1,33 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material Transfer to manufacture", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {purpose:'Material Transfer for Manufacture'},
- {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {to_warehouse:'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 1},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.total_outgoing_value==100, " Outgoing Value correct");
- assert.ok(cur_frm.doc.total_incoming_value==100, " Incoming Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
deleted file mode 100644
index 20f119a..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
+++ /dev/null
@@ -1,41 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test repack", function(assert) {
- assert.expect(2);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {purpose:'Repack'},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 1},
- {'s_warehouse':'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- ],
- [
- {'item_code': 'Test Product 2'},
- {'qty': 1},
- {'s_warehouse':'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- ],
- [
- {'item_code': 'Test Product 3'},
- {'qty': 1},
- {'t_warehouse':'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- ],
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.total_outgoing_value==250, " Outgoing Value correct");
- assert.ok(cur_frm.doc.total_incoming_value==250, " Incoming Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
deleted file mode 100644
index 8243426..0000000
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
+++ /dev/null
@@ -1,33 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test material Transfer to manufacture", function(assert) {
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Stock Entry', [
- {purpose:'Send to Subcontractor'},
- {from_warehouse:'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {to_warehouse:'Finished Goods - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
- {items: [
- [
- {'item_code': 'Test Product 1'},
- {'qty': 1},
- ]
- ]},
- ]);
- },
- () => cur_frm.save(),
- () => frappe.click_button('Update Rate and Availability'),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
- assert.ok(cur_frm.doc.total_outgoing_value==100, " Outgoing Value correct");
- assert.ok(cur_frm.doc.total_incoming_value==100, " Incoming Value correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index 2651407..46ce9de 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -150,7 +150,7 @@
"fieldtype": "Float",
"in_filter": 1,
"in_list_view": 1,
- "label": "Actual Quantity",
+ "label": "Qty Change",
"oldfieldname": "actual_qty",
"oldfieldtype": "Currency",
"print_width": "150px",
@@ -189,7 +189,7 @@
"fieldname": "qty_after_transaction",
"fieldtype": "Float",
"in_filter": 1,
- "label": "Actual Qty After Transaction",
+ "label": "Qty After Transaction",
"oldfieldname": "bin_aqat",
"oldfieldtype": "Currency",
"print_width": "150px",
@@ -210,7 +210,7 @@
{
"fieldname": "stock_value",
"fieldtype": "Currency",
- "label": "Stock Value",
+ "label": "Balance Stock Value",
"oldfieldname": "stock_value",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
@@ -219,14 +219,14 @@
{
"fieldname": "stock_value_difference",
"fieldtype": "Currency",
- "label": "Stock Value Difference",
+ "label": "Change in Stock Value",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "stock_queue",
"fieldtype": "Text",
- "label": "Stock Queue (FIFO)",
+ "label": "FIFO Stock Queue (qty, rate)",
"oldfieldname": "fcfs_stack",
"oldfieldtype": "Text",
"print_hide": 1,
@@ -317,10 +317,11 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-10-08 13:42:51.857631",
+ "modified": "2021-12-21 06:25:30.040801",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -338,5 +339,6 @@
}
],
"sort_field": "modified",
- "sort_order": "DESC"
-}
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
deleted file mode 100644
index 666d2c7..0000000
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
+++ /dev/null
@@ -1,31 +0,0 @@
-QUnit.module('Stock');
-
-QUnit.test("test Stock Reconciliation", function(assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('List', 'Stock Reconciliation'),
- () => frappe.timeout(1),
- () => frappe.click_button('New'),
- () => cur_frm.set_value('company','For Testing'),
- () => frappe.click_button('Items'),
- () => {cur_dialog.set_value('warehouse','Stores - FT'); },
- () => frappe.timeout(0.5),
- () => frappe.click_button('Update'),
- () => {
- cur_frm.doc.items[0].qty = 150;
- cur_frm.refresh_fields('items');},
- () => frappe.timeout(0.5),
- () => cur_frm.set_value('expense_account','Stock Adjustment - FT'),
- () => cur_frm.set_value('cost_center','Main - FT'),
- () => cur_frm.save(),
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.expense_account=='Stock Adjustment - FT', "expense_account correct");
- },
- () => frappe.tests.click_button('Submit'),
- () => frappe.tests.click_button('Yes'),
- () => frappe.timeout(0.3),
- () => done()
- ]);
-});
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 48e339a..c4ddc9e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -24,11 +24,15 @@
class TestStockReconciliation(ERPNextTestCase):
@classmethod
- def setUpClass(self):
+ def setUpClass(cls):
super().setUpClass()
create_batch_or_serial_no_items()
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
+ def tearDown(self):
+ frappe.flags.dont_execute_stock_reposts = None
+
+
def test_reco_for_fifo(self):
self._test_reco_sle_gle("FIFO")
@@ -392,6 +396,41 @@
repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
+ def test_intermediate_sr_bin_update(self):
+ """Bin should show correct qty even for backdated entries.
+
+ -------------------------------------------
+ | creation | Var | Doc | Qty | balance qty
+ -------------------------------------------
+ | 1 | SR | Reco | 10 | 10 (posting date: today+10)
+ | 3 | SR2 | Reco | 11 | 11 (posting date: today+11)
+ | 2 | DN | DN | 5 | 6 <-- assert in BIN (posting date: today+12)
+ """
+ from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+ # repost will make this test useless, qty should update in realtime without reposts
+ frappe.flags.dont_execute_stock_reposts = True
+ frappe.db.rollback()
+
+ item_code = "Backdated-Reco-Cancellation-Item"
+ warehouse = "_Test Warehouse - _TC"
+ create_item(item_code)
+
+ sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
+ posting_date=add_days(nowdate(), 10))
+
+ dn = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=5, rate=120,
+ posting_date=add_days(nowdate(), 12))
+ old_bin_qty = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
+
+ sr2 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=11, rate=100,
+ posting_date=add_days(nowdate(), 11))
+ new_bin_qty = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
+
+ self.assertEqual(old_bin_qty + 1, new_bin_qty)
+ frappe.db.rollback()
+
+
def test_valid_batch(self):
create_batch_item_with_batch("Testing Batch Item 1", "001")
create_batch_item_with_batch("Testing Batch Item 2", "002")
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.js b/erpnext/stock/doctype/warehouse/test_warehouse.js
deleted file mode 100644
index 850da1e..0000000
--- a/erpnext/stock/doctype/warehouse/test_warehouse.js
+++ /dev/null
@@ -1,19 +0,0 @@
-QUnit.test("test: warehouse", function (assert) {
- assert.expect(0);
- let done = assert.async();
-
- frappe.run_serially([
- // test warehouse creation
- () => frappe.set_route("List", "Warehouse"),
-
- // Create a Laptop Scrap Warehouse
- () => frappe.tests.make(
- "Warehouse", [
- {warehouse_name: "Laptop Scrap Warehouse"},
- {company: "For Testing"}
- ]
- ),
-
- () => done()
- ]);
-});
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 9889a22..06f8fa7 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -1097,7 +1097,7 @@
}
def apply_price_list_on_item(args):
- item_doc = frappe.get_doc("Item", args.item_code)
+ item_doc = frappe.db.get_value("Item", args.item_code, ['name', 'variant_of'], as_dict=1)
item_details = get_price_list_rate(args, item_doc)
item_details.update(get_pricing_rule_for_item(args, item_details.price_list_rate))
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 0ebe4f9..e6dfc97 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -3,6 +3,7 @@
from operator import itemgetter
+from typing import Dict, List, Tuple, Union
import frappe
from frappe import _
@@ -10,19 +11,29 @@
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+Filters = frappe._dict
-def execute(filters=None):
- columns = get_columns(filters)
- item_details = get_fifo_queue(filters)
+def execute(filters: Filters = None) -> Tuple:
to_date = filters["to_date"]
- _func = itemgetter(1)
+ columns = get_columns(filters)
+ item_details = FIFOSlots(filters).generate()
+ data = format_report_data(filters, item_details, to_date)
+
+ chart_data = get_chart_data(data, filters)
+
+ return columns, data, None, chart_data
+
+def format_report_data(filters: Filters, item_details: Dict, to_date: str) -> List[Dict]:
+ "Returns ordered, formatted data with ranges."
+ _func = itemgetter(1)
data = []
+
for item, item_dict in item_details.items():
earliest_age, latest_age = 0, 0
+ details = item_dict["details"]
fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func)
- details = item_dict["details"]
if not fifo_queue: continue
@@ -31,23 +42,22 @@
latest_age = date_diff(to_date, fifo_queue[-1][1])
range1, range2, range3, above_range3 = get_range_age(filters, fifo_queue, to_date, item_dict)
- row = [details.name, details.item_name,
- details.description, details.item_group, details.brand]
+ row = [details.name, details.item_name, details.description,
+ details.item_group, details.brand]
if filters.get("show_warehouse_wise_stock"):
row.append(details.warehouse)
row.extend([item_dict.get("total_qty"), average_age,
range1, range2, range3, above_range3,
- earliest_age, latest_age, details.stock_uom])
+ earliest_age, latest_age,
+ details.stock_uom])
data.append(row)
- chart_data = get_chart_data(data, filters)
+ return data
- return columns, data, None, chart_data
-
-def get_average_age(fifo_queue, to_date):
+def get_average_age(fifo_queue: List, to_date: str) -> float:
batch_age = age_qty = total_qty = 0.0
for batch in fifo_queue:
batch_age = date_diff(to_date, batch[1])
@@ -61,7 +71,7 @@
return flt(age_qty / total_qty, 2) if total_qty else 0.0
-def get_range_age(filters, fifo_queue, to_date, item_dict):
+def get_range_age(filters: Filters, fifo_queue: List, to_date: str, item_dict: Dict) -> Tuple:
range1 = range2 = range3 = above_range3 = 0.0
for item in fifo_queue:
@@ -79,7 +89,7 @@
return range1, range2, range3, above_range3
-def get_columns(filters):
+def get_columns(filters: Filters) -> List[Dict]:
range_columns = []
setup_ageing_columns(filters, range_columns)
columns = [
@@ -164,106 +174,7 @@
return columns
-def get_fifo_queue(filters, sle=None):
- item_details = {}
- transferred_item_details = {}
- serial_no_batch_purchase_details = {}
-
- if sle == None:
- sle = get_stock_ledger_entries(filters)
-
- for d in sle:
- key = (d.name, d.warehouse) if filters.get('show_warehouse_wise_stock') else d.name
- item_details.setdefault(key, {"details": d, "fifo_queue": []})
- fifo_queue = item_details[key]["fifo_queue"]
-
- transferred_item_key = (d.voucher_no, d.name, d.warehouse)
- transferred_item_details.setdefault(transferred_item_key, [])
-
- if d.voucher_type == "Stock Reconciliation":
- d.actual_qty = flt(d.qty_after_transaction) - flt(item_details[key].get("qty_after_transaction", 0))
-
- serial_no_list = get_serial_nos(d.serial_no) if d.serial_no else []
-
- if d.actual_qty > 0:
- if transferred_item_details.get(transferred_item_key):
- batch = transferred_item_details[transferred_item_key][0]
- fifo_queue.append(batch)
- transferred_item_details[transferred_item_key].pop(0)
- else:
- if serial_no_list:
- for serial_no in serial_no_list:
- if serial_no_batch_purchase_details.get(serial_no):
- fifo_queue.append([serial_no, serial_no_batch_purchase_details.get(serial_no)])
- else:
- serial_no_batch_purchase_details.setdefault(serial_no, d.posting_date)
- fifo_queue.append([serial_no, d.posting_date])
- else:
- fifo_queue.append([d.actual_qty, d.posting_date])
- else:
- if serial_no_list:
- fifo_queue[:] = [serial_no for serial_no in fifo_queue if serial_no[0] not in serial_no_list]
- else:
- qty_to_pop = abs(d.actual_qty)
- while qty_to_pop:
- batch = fifo_queue[0] if fifo_queue else [0, None]
- if 0 < flt(batch[0]) <= qty_to_pop:
- # if batch qty > 0
- # not enough or exactly same qty in current batch, clear batch
- qty_to_pop -= flt(batch[0])
- transferred_item_details[transferred_item_key].append(fifo_queue.pop(0))
- else:
- # all from current batch
- batch[0] = flt(batch[0]) - qty_to_pop
- transferred_item_details[transferred_item_key].append([qty_to_pop, batch[1]])
- qty_to_pop = 0
-
- item_details[key]["qty_after_transaction"] = d.qty_after_transaction
-
- if "total_qty" not in item_details[key]:
- item_details[key]["total_qty"] = d.actual_qty
- else:
- item_details[key]["total_qty"] += d.actual_qty
-
- item_details[key]["has_serial_no"] = d.has_serial_no
-
- return item_details
-
-def get_stock_ledger_entries(filters):
- return frappe.db.sql("""select
- item.name, item.item_name, item_group, brand, description, item.stock_uom, item.has_serial_no,
- actual_qty, posting_date, voucher_type, voucher_no, serial_no, batch_no, qty_after_transaction, warehouse
- from `tabStock Ledger Entry` sle,
- (select name, item_name, description, stock_uom, brand, item_group, has_serial_no
- from `tabItem` {item_conditions}) item
- where item_code = item.name and
- company = %(company)s and
- posting_date <= %(to_date)s and
- is_cancelled != 1
- {sle_conditions}
- order by posting_date, posting_time, sle.creation, actual_qty""" #nosec
- .format(item_conditions=get_item_conditions(filters),
- sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
-
-def get_item_conditions(filters):
- conditions = []
- if filters.get("item_code"):
- conditions.append("item_code=%(item_code)s")
- if filters.get("brand"):
- conditions.append("brand=%(brand)s")
-
- return "where {}".format(" and ".join(conditions)) if conditions else ""
-
-def get_sle_conditions(filters):
- conditions = []
- if filters.get("warehouse"):
- lft, rgt = frappe.db.get_value('Warehouse', filters.get("warehouse"), ['lft', 'rgt'])
- conditions.append("""warehouse in (select wh.name from `tabWarehouse` wh
- where wh.lft >= {0} and rgt <= {1})""".format(lft, rgt))
-
- return "and {}".format(" and ".join(conditions)) if conditions else ""
-
-def get_chart_data(data, filters):
+def get_chart_data(data: List, filters: Filters) -> Dict:
if not data:
return []
@@ -294,17 +205,201 @@
"type" : "bar"
}
-def setup_ageing_columns(filters, range_columns):
- for i, label in enumerate(["0-{range1}".format(range1=filters["range1"]),
- "{range1}-{range2}".format(range1=cint(filters["range1"])+ 1, range2=filters["range2"]),
- "{range2}-{range3}".format(range2=cint(filters["range2"])+ 1, range3=filters["range3"]),
- "{range3}-{above}".format(range3=cint(filters["range3"])+ 1, above=_("Above"))]):
- add_column(range_columns, label="Age ("+ label +")", fieldname='range' + str(i+1))
+def setup_ageing_columns(filters: Filters, range_columns: List):
+ ranges = [
+ f"0 - {filters['range1']}",
+ f"{cint(filters['range1']) + 1} - {cint(filters['range2'])}",
+ f"{cint(filters['range2']) + 1} - {cint(filters['range3'])}",
+ f"{cint(filters['range3']) + 1} - {_('Above')}"
+ ]
+ for i, label in enumerate(ranges):
+ fieldname = 'range' + str(i+1)
+ add_column(range_columns, label=f"Age ({label})",fieldname=fieldname)
-def add_column(range_columns, label, fieldname, fieldtype='Float', width=140):
+def add_column(range_columns: List, label:str, fieldname: str, fieldtype: str = 'Float', width: int = 140):
range_columns.append(dict(
label=label,
fieldname=fieldname,
fieldtype=fieldtype,
width=width
))
+
+
+class FIFOSlots:
+ "Returns FIFO computed slots of inwarded stock as per date."
+
+ def __init__(self, filters: Dict = None , sle: List = None):
+ self.item_details = {}
+ self.transferred_item_details = {}
+ self.serial_no_batch_purchase_details = {}
+ self.filters = filters
+ self.sle = sle
+
+ def generate(self) -> Dict:
+ """
+ Returns dict of the foll.g structure:
+ Key = Item A / (Item A, Warehouse A)
+ Key: {
+ 'details' -> Dict: ** item details **,
+ 'fifo_queue' -> List: ** list of lists containing entries/slots for existing stock,
+ consumed/updated and maintained via FIFO. **
+ }
+ """
+ if self.sle is None:
+ self.sle = self.__get_stock_ledger_entries()
+
+ for d in self.sle:
+ key, fifo_queue, transferred_item_key = self.__init_key_stores(d)
+
+ if d.voucher_type == "Stock Reconciliation":
+ prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
+ d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
+
+ serial_nos = get_serial_nos(d.serial_no) if d.serial_no else []
+
+ if d.actual_qty > 0:
+ self.__compute_incoming_stock(d, fifo_queue, transferred_item_key, serial_nos)
+ else:
+ self.__compute_outgoing_stock(d, fifo_queue, transferred_item_key, serial_nos)
+
+ self.__update_balances(d, key)
+
+ return self.item_details
+
+ def __init_key_stores(self, row: Dict) -> Tuple:
+ "Initialise keys and FIFO Queue."
+
+ key = (row.name, row.warehouse) if self.filters.get('show_warehouse_wise_stock') else row.name
+ self.item_details.setdefault(key, {"details": row, "fifo_queue": []})
+ fifo_queue = self.item_details[key]["fifo_queue"]
+
+ transferred_item_key = (row.voucher_no, row.name, row.warehouse)
+ self.transferred_item_details.setdefault(transferred_item_key, [])
+
+ return key, fifo_queue, transferred_item_key
+
+ def __compute_incoming_stock(self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List):
+ "Update FIFO Queue on inward stock."
+
+ if self.transferred_item_details.get(transfer_key):
+ # inward/outward from same voucher, item & warehouse
+ slot = self.transferred_item_details[transfer_key].pop(0)
+ fifo_queue.append(slot)
+ else:
+ if not serial_nos:
+ if fifo_queue and flt(fifo_queue[0][0]) < 0:
+ # neutralize negative stock by adding positive stock
+ fifo_queue[0][0] += flt(row.actual_qty)
+ fifo_queue[0][1] = row.posting_date
+ else:
+ fifo_queue.append([flt(row.actual_qty), row.posting_date])
+ return
+
+ for serial_no in serial_nos:
+ if self.serial_no_batch_purchase_details.get(serial_no):
+ fifo_queue.append([serial_no, self.serial_no_batch_purchase_details.get(serial_no)])
+ else:
+ self.serial_no_batch_purchase_details.setdefault(serial_no, row.posting_date)
+ fifo_queue.append([serial_no, row.posting_date])
+
+ def __compute_outgoing_stock(self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List):
+ "Update FIFO Queue on outward stock."
+ if serial_nos:
+ fifo_queue[:] = [serial_no for serial_no in fifo_queue if serial_no[0] not in serial_nos]
+ return
+
+ qty_to_pop = abs(row.actual_qty)
+ while qty_to_pop:
+ slot = fifo_queue[0] if fifo_queue else [0, None]
+ if 0 < flt(slot[0]) <= qty_to_pop:
+ # qty to pop >= slot qty
+ # if +ve and not enough or exactly same balance in current slot, consume whole slot
+ qty_to_pop -= flt(slot[0])
+ self.transferred_item_details[transfer_key].append(fifo_queue.pop(0))
+ elif not fifo_queue:
+ # negative stock, no balance but qty yet to consume
+ fifo_queue.append([-(qty_to_pop), row.posting_date])
+ self.transferred_item_details[transfer_key].append([row.actual_qty, row.posting_date])
+ qty_to_pop = 0
+ else:
+ # qty to pop < slot qty, ample balance
+ # consume actual_qty from first slot
+ slot[0] = flt(slot[0]) - qty_to_pop
+ self.transferred_item_details[transfer_key].append([qty_to_pop, slot[1]])
+ qty_to_pop = 0
+
+ def __update_balances(self, row: Dict, key: Union[Tuple, str]):
+ self.item_details[key]["qty_after_transaction"] = row.qty_after_transaction
+
+ if "total_qty" not in self.item_details[key]:
+ self.item_details[key]["total_qty"] = row.actual_qty
+ else:
+ self.item_details[key]["total_qty"] += row.actual_qty
+
+ self.item_details[key]["has_serial_no"] = row.has_serial_no
+
+ def __get_stock_ledger_entries(self) -> List[Dict]:
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+ item = self.__get_item_query() # used as derived table in sle query
+
+ sle_query = (
+ frappe.qb.from_(sle).from_(item)
+ .select(
+ item.name, item.item_name, item.item_group,
+ item.brand, item.description,
+ item.stock_uom, item.has_serial_no,
+ sle.actual_qty, sle.posting_date,
+ sle.voucher_type, sle.voucher_no,
+ sle.serial_no, sle.batch_no,
+ sle.qty_after_transaction, sle.warehouse
+ ).where(
+ (sle.item_code == item.name)
+ & (sle.company == self.filters.get("company"))
+ & (sle.posting_date <= self.filters.get("to_date"))
+ & (sle.is_cancelled != 1)
+ )
+ )
+
+ if self.filters.get("warehouse"):
+ sle_query = self.__get_warehouse_conditions(sle, sle_query)
+
+ sle_query = sle_query.orderby(
+ sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty
+ )
+
+ return sle_query.run(as_dict=True)
+
+ def __get_item_query(self) -> str:
+ item_table = frappe.qb.DocType("Item")
+
+ item = frappe.qb.from_("Item").select(
+ "name", "item_name", "description", "stock_uom",
+ "brand", "item_group", "has_serial_no"
+ )
+
+ if self.filters.get("item_code"):
+ item = item.where(item_table.item_code == self.filters.get("item_code"))
+
+ if self.filters.get("brand"):
+ item = item.where(item_table.brand == self.filters.get("brand"))
+
+ return item
+
+ def __get_warehouse_conditions(self, sle, sle_query) -> str:
+ warehouse = frappe.qb.DocType("Warehouse")
+ lft, rgt = frappe.db.get_value(
+ "Warehouse",
+ self.filters.get("warehouse"),
+ ['lft', 'rgt']
+ )
+
+ warehouse_results = (
+ frappe.qb.from_(warehouse)
+ .select("name").where(
+ (warehouse.lft >= lft)
+ & (warehouse.rgt <= rgt)
+ ).run()
+ )
+ warehouse_results = [x[0] for x in warehouse_results]
+
+ return sle_query.where(sle.warehouse.isin(warehouse_results))
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md b/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md
new file mode 100644
index 0000000..5ffe97f
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md
@@ -0,0 +1,73 @@
+### Concept of FIFO Slots
+
+Since we need to know age-wise remaining stock, we maintain all the inward entries as slots. So each time stock comes in, a slot is added for the same.
+
+Eg. For Item A:
+----------------------
+Date | Qty | Queue
+----------------------
+1st | +50 | [[50, 1-12-2021]]
+2nd | +20 | [[50, 1-12-2021], [20, 2-12-2021]]
+----------------------
+
+Now the queue can tell us the total stock and also how old the stock is.
+Here, the balance qty is 70.
+50 qty is (today-the 1st) days old
+20 qty is (today-the 2nd) days old
+
+### Calculation of FIFO Slots
+
+#### Case 1: Outward from sufficient balance qty
+----------------------
+Date | Qty | Queue
+----------------------
+1st | +50 | [[50, 1-12-2021]]
+2nd | -20 | [[30, 1-12-2021]]
+2nd | +20 | [[30, 1-12-2021], [20, 2-12-2021]]
+
+Here after the first entry, while issuing 20 qty:
+- **since 20 is lesser than the balance**, **qty_to_pop (20)** is simply consumed from first slot (FIFO consumption)
+- Any inward entry after as usual will get its own slot added to the queue
+
+#### Case 2: Outward from sufficient cumulative (slots) balance qty
+----------------------
+Date | Qty | Queue
+----------------------
+1st | +50 | [[50, 1-12-2021]]
+2nd | +20 | [[50, 1-12-2021], [20, 2-12-2021]]
+2nd | -60 | [[10, 2-12-2021]]
+
+- Consumption happens slot wise. First slot 1 is consumed
+- Since **qty_to_pop (60) is greater than slot 1 qty (50)**, the entire slot is consumed and popped
+- Now the queue is [[20, 2-12-2021]], and **qty_to_pop=10** (remaining qty to pop)
+- It then goes ahead to the next slot and consumes 10 from it
+- Now the queue is [[10, 2-12-2021]]
+
+#### Case 3: Outward from insufficient balance qty
+> This case is possible only if **Allow Negative Stock** was enabled at some point/is enabled.
+
+----------------------
+Date | Qty | Queue
+----------------------
+1st | +50 | [[50, 1-12-2021]]
+2nd | -60 | [[-10, 1-12-2021]]
+
+- Since **qty_to_pop (60)** is more than the balance in slot 1, the entire slot is consumed and popped
+- Now the queue is **empty**, and **qty_to_pop=10** (remaining qty to pop)
+- Since we still have more to consume, we append the balance since 60 is issued from 50 i.e. -10.
+- We register this negative value, since the stock issue has caused the balance to become negative
+
+Now when stock is inwarded:
+- Instead of adding a slot we check if there are any negative balances.
+- If yes, we keep adding positive stock to it until we make the balance positive.
+- Once the balance is positive, the next inward entry will add a new slot in the queue
+
+Eg:
+----------------------
+Date | Qty | Queue
+----------------------
+1st | +50 | [[50, 1-12-2021]]
+2nd | -60 | [[-10, 1-12-2021]]
+3rd | +5 | [[-5, 3-12-2021]]
+4th | +10 | [[5, 4-12-2021]]
+4th | +20 | [[5, 4-12-2021], [20, 4-12-2021]]
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
new file mode 100644
index 0000000..949bb7c
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
@@ -0,0 +1,126 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+
+from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestStockAgeing(ERPNextTestCase):
+ def setUp(self) -> None:
+ self.filters = frappe._dict(
+ company="_Test Company",
+ to_date="2021-12-10"
+ )
+
+ def test_normal_inward_outward_queue(self):
+ "Reference: Case 1 in stock_ageing_fifo_logic.md"
+ sle = [
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=30, qty_after_transaction=30,
+ posting_date="2021-12-01", voucher_type="Stock Entry",
+ voucher_no="001",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=20, qty_after_transaction=50,
+ posting_date="2021-12-02", voucher_type="Stock Entry",
+ voucher_no="002",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=(-10), qty_after_transaction=40,
+ posting_date="2021-12-03", voucher_type="Stock Entry",
+ voucher_no="003",
+ has_serial_no=False, serial_no=None
+ )
+ ]
+
+ slots = FIFOSlots(self.filters, sle).generate()
+
+ self.assertTrue(slots["Flask Item"]["fifo_queue"])
+ result = slots["Flask Item"]
+ queue = result["fifo_queue"]
+
+ self.assertEqual(result["qty_after_transaction"], result["total_qty"])
+ self.assertEqual(queue[0][0], 20.0)
+
+ def test_insufficient_balance(self):
+ "Reference: Case 3 in stock_ageing_fifo_logic.md"
+ sle = [
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=(-30), qty_after_transaction=(-30),
+ posting_date="2021-12-01", voucher_type="Stock Entry",
+ voucher_no="001",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=20, qty_after_transaction=(-10),
+ posting_date="2021-12-02", voucher_type="Stock Entry",
+ voucher_no="002",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=20, qty_after_transaction=10,
+ posting_date="2021-12-03", voucher_type="Stock Entry",
+ voucher_no="003",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=10, qty_after_transaction=20,
+ posting_date="2021-12-03", voucher_type="Stock Entry",
+ voucher_no="004",
+ has_serial_no=False, serial_no=None
+ )
+ ]
+
+ slots = FIFOSlots(self.filters, sle).generate()
+
+ result = slots["Flask Item"]
+ queue = result["fifo_queue"]
+
+ self.assertEqual(result["qty_after_transaction"], result["total_qty"])
+ self.assertEqual(queue[0][0], 10.0)
+ self.assertEqual(queue[1][0], 10.0)
+
+ def test_stock_reconciliation(self):
+ sle = [
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=30, qty_after_transaction=30,
+ posting_date="2021-12-01", voucher_type="Stock Entry",
+ voucher_no="001",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=0, qty_after_transaction=50,
+ posting_date="2021-12-02", voucher_type="Stock Reconciliation",
+ voucher_no="002",
+ has_serial_no=False, serial_no=None
+ ),
+ frappe._dict(
+ name="Flask Item",
+ actual_qty=(-10), qty_after_transaction=40,
+ posting_date="2021-12-03", voucher_type="Stock Entry",
+ voucher_no="003",
+ has_serial_no=False, serial_no=None
+ )
+ ]
+
+ slots = FIFOSlots(self.filters, sle).generate()
+
+ result = slots["Flask Item"]
+ queue = result["fifo_queue"]
+
+ self.assertEqual(result["qty_after_transaction"], result["total_qty"])
+ self.assertEqual(queue[0][0], 20.0)
+ self.assertEqual(queue[1][0], 20.0)
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 3c7b26b..b4f43a7 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -9,7 +9,7 @@
from frappe.utils import cint, date_diff, flt, getdate
import erpnext
-from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue
+from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress
@@ -33,7 +33,7 @@
if filters.get('show_stock_ageing_data'):
filters['show_warehouse_wise_stock'] = True
- item_wise_fifo_queue = get_fifo_queue(filters, sle)
+ item_wise_fifo_queue = FIFOSlots(filters, sle).generate()
# if no stock ledger entry found return
if not sle:
diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js
index c484516..31f389f 100644
--- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js
+++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js
@@ -8,7 +8,8 @@
"fifo_value_diff",
"fifo_valuation_diff",
"valuation_diff",
- "fifo_difference_diff"
+ "fifo_difference_diff",
+ "diff_value_diff"
];
frappe.query_reports["Stock Ledger Invariant Check"] = {
diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
index ca47a1e..48753b0 100644
--- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
+++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
@@ -50,6 +50,7 @@
def add_invariant_check_fields(sles):
balance_qty = 0.0
+ balance_stock_value = 0.0
for idx, sle in enumerate(sles):
queue = json.loads(sle.stock_queue)
@@ -60,6 +61,7 @@
fifo_value += qty * rate
balance_qty += sle.actual_qty
+ balance_stock_value += sle.stock_value_difference
if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
balance_qty = sle.qty_after_transaction
@@ -70,6 +72,7 @@
sle.stock_value / sle.qty_after_transaction if sle.qty_after_transaction else None
)
sle.expected_qty_after_transaction = balance_qty
+ sle.stock_value_from_diff = balance_stock_value
# set difference fields
sle.difference_in_qty = sle.qty_after_transaction - sle.expected_qty_after_transaction
@@ -81,6 +84,7 @@
sle.valuation_diff = (
sle.valuation_rate - sle.balance_value_by_qty if sle.balance_value_by_qty else None
)
+ sle.diff_value_diff = sle.stock_value_from_diff - sle.stock_value
if idx > 0:
sle.fifo_stock_diff = sle.fifo_stock_value - sles[idx - 1].fifo_stock_value
@@ -191,13 +195,22 @@
"fieldtype": "Float",
"label": "D - E",
},
-
{
"fieldname": "stock_value_difference",
"fieldtype": "Float",
"label": "(F) Stock Value Difference",
},
{
+ "fieldname": "stock_value_from_diff",
+ "fieldtype": "Float",
+ "label": "Balance Stock Value using (F)",
+ },
+ {
+ "fieldname": "diff_value_diff",
+ "fieldtype": "Float",
+ "label": "K - D",
+ },
+ {
"fieldname": "fifo_stock_diff",
"fieldtype": "Float",
"label": "(G) Stock Value difference (FIFO queue)",
diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
index 4d1491b..22bdb89 100644
--- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
+++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
@@ -9,7 +9,7 @@
from frappe import _
from frappe.utils import flt
-from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue
+from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age
from erpnext.stock.report.stock_balance.stock_balance import (
get_item_details,
get_item_warehouse_map,
@@ -33,7 +33,7 @@
item_map = get_item_details(items, sle, filters)
iwb_map = get_item_warehouse_map(filters, sle)
warehouse_list = get_warehouse_list(filters)
- item_ageing = get_fifo_queue(filters)
+ item_ageing = FIFOSlots(filters).generate()
data = []
item_balance = {}
item_value = {}
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index e95c0fc..107bb23 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -16,6 +16,7 @@
get_or_make_bin,
get_valuation_method,
)
+from erpnext.stock.valuation import FIFOValuation
class NegativeStockError(frappe.ValidationError): pass
@@ -64,8 +65,8 @@
is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
if is_stock_item:
bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
- update_bin_qty(bin_name, args)
repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher)
+ update_bin_qty(bin_name, args)
else:
frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
@@ -456,9 +457,8 @@
self.wh_data.qty_after_transaction += flt(sle.actual_qty)
self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
else:
- self.get_fifo_values(sle)
+ self.update_fifo_values(sle)
self.wh_data.qty_after_transaction += flt(sle.actual_qty)
- self.wh_data.stock_value = sum(flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue)
# rounding as per precision
self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision)
@@ -696,87 +696,39 @@
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
currency=erpnext.get_company_currency(sle.company), company=sle.company)
- def get_fifo_values(self, sle):
+ def update_fifo_values(self, sle):
incoming_rate = flt(sle.incoming_rate)
actual_qty = flt(sle.actual_qty)
outgoing_rate = flt(sle.outgoing_rate)
+ fifo_queue = FIFOValuation(self.wh_data.stock_queue)
if actual_qty > 0:
- if not self.wh_data.stock_queue:
- self.wh_data.stock_queue.append([0, 0])
-
- # last row has the same rate, just updated the qty
- if self.wh_data.stock_queue[-1][1]==incoming_rate:
- self.wh_data.stock_queue[-1][0] += actual_qty
- else:
- # Item has a positive balance qty, add new entry
- if self.wh_data.stock_queue[-1][0] > 0:
- self.wh_data.stock_queue.append([actual_qty, incoming_rate])
- else: # negative balance qty
- qty = self.wh_data.stock_queue[-1][0] + actual_qty
- if qty > 0: # new balance qty is positive
- self.wh_data.stock_queue[-1] = [qty, incoming_rate]
- else: # new balance qty is still negative, maintain same rate
- self.wh_data.stock_queue[-1][0] = qty
+ fifo_queue.add_stock(qty=actual_qty, rate=incoming_rate)
else:
- qty_to_pop = abs(actual_qty)
- while qty_to_pop:
- if not self.wh_data.stock_queue:
- # Get valuation rate from last sle if exists or from valuation rate field in item master
- allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
- if not allow_zero_valuation_rate:
- _rate = get_valuation_rate(sle.item_code, sle.warehouse,
- sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company), company=sle.company)
- else:
- _rate = 0
-
- self.wh_data.stock_queue.append([0, _rate])
-
- index = None
- if outgoing_rate > 0:
- # Find the entry where rate matched with outgoing rate
- for i, v in enumerate(self.wh_data.stock_queue):
- if v[1] == outgoing_rate:
- index = i
- break
-
- # If no entry found with outgoing rate, collapse stack
- if index is None: # nosemgrep
- new_stock_value = sum(d[0]*d[1] for d in self.wh_data.stock_queue) - qty_to_pop*outgoing_rate
- new_stock_qty = sum(d[0] for d in self.wh_data.stock_queue) - qty_to_pop
- self.wh_data.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
- break
+ def rate_generator() -> float:
+ allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
+ if not allow_zero_valuation_rate:
+ return get_valuation_rate(sle.item_code, sle.warehouse,
+ sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
else:
- index = 0
+ return 0.0
- # select first batch or the batch with same rate
- batch = self.wh_data.stock_queue[index]
- if qty_to_pop >= batch[0]:
- # consume current batch
- qty_to_pop = _round_off_if_near_zero(qty_to_pop - batch[0])
- self.wh_data.stock_queue.pop(index)
- if not self.wh_data.stock_queue and qty_to_pop:
- # stock finished, qty still remains to be withdrawn
- # negative stock, keep in as a negative batch
- self.wh_data.stock_queue.append([-qty_to_pop, outgoing_rate or batch[1]])
- break
+ fifo_queue.remove_stock(qty=abs(actual_qty), outgoing_rate=outgoing_rate, rate_generator=rate_generator)
- else:
- # qty found in current batch
- # consume it and exit
- batch[0] = batch[0] - qty_to_pop
- qty_to_pop = 0
+ stock_qty, stock_value = fifo_queue.get_total_stock_and_value()
- stock_value = _round_off_if_near_zero(sum(flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue))
- stock_qty = _round_off_if_near_zero(sum(flt(batch[0]) for batch in self.wh_data.stock_queue))
-
+ self.wh_data.stock_queue = fifo_queue.get_state()
+ self.wh_data.stock_value = stock_value
if stock_qty:
- self.wh_data.valuation_rate = stock_value / flt(stock_qty)
+ self.wh_data.valuation_rate = stock_value / stock_qty
+
if not self.wh_data.stock_queue:
self.wh_data.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.wh_data.valuation_rate])
+
+
def check_if_allow_zero_valuation_rate(self, voucher_type, voucher_detail_no):
ref_item_dt = ""
@@ -1158,13 +1110,3 @@
and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
limit 1
""", args, as_dict=1)
-
-
-def _round_off_if_near_zero(number: float, precision: int = 6) -> float:
- """ Rounds off the number to zero only if number is close to zero for decimal
- specified in precision. Precision defaults to 6.
- """
- if flt(number) < (1.0 / (10**precision)):
- return 0
-
- return flt(number)
diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py
new file mode 100644
index 0000000..85788ba
--- /dev/null
+++ b/erpnext/stock/tests/test_valuation.py
@@ -0,0 +1,166 @@
+import unittest
+
+from hypothesis import given
+from hypothesis import strategies as st
+
+from erpnext.stock.valuation import FIFOValuation, _round_off_if_near_zero
+
+qty_gen = st.floats(min_value=-1e6, max_value=1e6)
+value_gen = st.floats(min_value=1, max_value=1e6)
+stock_queue_generator = st.lists(st.tuples(qty_gen, value_gen), min_size=10)
+
+
+class TestFifoValuation(unittest.TestCase):
+
+ def setUp(self):
+ self.queue = FIFOValuation([])
+
+ def tearDown(self):
+ qty, value = self.queue.get_total_stock_and_value()
+ self.assertTotalQty(qty)
+ self.assertTotalValue(value)
+
+ def assertTotalQty(self, qty):
+ self.assertAlmostEqual(sum(q for q, _ in self.queue), qty, msg=f"queue: {self.queue}", places=4)
+
+ def assertTotalValue(self, value):
+ self.assertAlmostEqual(sum(q * r for q, r in self.queue), value, msg=f"queue: {self.queue}", places=2)
+
+ def test_simple_addition(self):
+ self.queue.add_stock(1, 10)
+ self.assertTotalQty(1)
+
+ def test_simple_removal(self):
+ self.queue.add_stock(1, 10)
+ self.queue.remove_stock(1)
+ self.assertTotalQty(0)
+
+ def test_merge_new_stock(self):
+ self.queue.add_stock(1, 10)
+ self.queue.add_stock(1, 10)
+ self.assertEqual(self.queue, [[2, 10]])
+
+ def test_adding_negative_stock_keeps_rate(self):
+ self.queue = FIFOValuation([[-5.0, 100]])
+ self.queue.add_stock(1, 10)
+ self.assertEqual(self.queue, [[-4, 100]])
+
+ def test_adding_negative_stock_updates_rate(self):
+ self.queue = FIFOValuation([[-5.0, 100]])
+ self.queue.add_stock(6, 10)
+ self.assertEqual(self.queue, [[1, 10]])
+
+
+ def test_negative_stock(self):
+ self.queue.remove_stock(1, 5)
+ self.assertEqual(self.queue, [[-1, 5]])
+
+ # XXX
+ self.queue.remove_stock(1, 10)
+ self.assertTotalQty(-2)
+
+ self.queue.add_stock(2, 10)
+ self.assertTotalQty(0)
+ self.assertTotalValue(0)
+
+ def test_removing_specified_rate(self):
+ self.queue.add_stock(1, 10)
+ self.queue.add_stock(1, 20)
+
+ self.queue.remove_stock(1, 20)
+ self.assertEqual(self.queue, [[1, 10]])
+
+
+ def test_remove_multiple_bins(self):
+ self.queue.add_stock(1, 10)
+ self.queue.add_stock(2, 20)
+ self.queue.add_stock(1, 20)
+ self.queue.add_stock(5, 20)
+
+ self.queue.remove_stock(4)
+ self.assertEqual(self.queue, [[5, 20]])
+
+
+ def test_remove_multiple_bins_with_rate(self):
+ self.queue.add_stock(1, 10)
+ self.queue.add_stock(2, 20)
+ self.queue.add_stock(1, 20)
+ self.queue.add_stock(5, 20)
+
+ self.queue.remove_stock(3, 20)
+ self.assertEqual(self.queue, [[1, 10], [5, 20]])
+
+ def test_collapsing_of_queue(self):
+ self.queue.add_stock(1, 1)
+ self.queue.add_stock(1, 2)
+ self.queue.add_stock(1, 3)
+ self.queue.add_stock(1, 4)
+
+ self.assertTotalValue(10)
+
+ self.queue.remove_stock(3, 1)
+ # XXX
+ self.assertEqual(self.queue, [[1, 7]])
+
+ def test_rounding_off(self):
+ self.queue.add_stock(1.0, 1.0)
+ self.queue.remove_stock(1.0 - 1e-9)
+ self.assertTotalQty(0)
+
+ def test_rounding_off_near_zero(self):
+ self.assertEqual(_round_off_if_near_zero(0), 0)
+ self.assertEqual(_round_off_if_near_zero(1), 1)
+ self.assertEqual(_round_off_if_near_zero(-1), -1)
+ self.assertEqual(_round_off_if_near_zero(-1e-8), 0)
+ self.assertEqual(_round_off_if_near_zero(1e-8), 0)
+
+ def test_totals(self):
+ self.queue.add_stock(1, 10)
+ self.queue.add_stock(2, 13)
+ self.queue.add_stock(1, 17)
+ self.queue.remove_stock(1)
+ self.queue.remove_stock(1)
+ self.queue.remove_stock(1)
+ self.queue.add_stock(5, 17)
+ self.queue.add_stock(8, 11)
+
+ @given(stock_queue_generator)
+ def test_fifo_qty_hypothesis(self, stock_queue):
+ self.queue = FIFOValuation([])
+ total_qty = 0
+
+ for qty, rate in stock_queue:
+ if qty == 0:
+ continue
+ if qty > 0:
+ self.queue.add_stock(qty, rate)
+ total_qty += qty
+ else:
+ qty = abs(qty)
+ consumed = self.queue.remove_stock(qty)
+ self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}")
+ total_qty -= qty
+ self.assertTotalQty(total_qty)
+
+ @given(stock_queue_generator)
+ def test_fifo_qty_value_nonneg_hypothesis(self, stock_queue):
+ self.queue = FIFOValuation([])
+ total_qty = 0.0
+ total_value = 0.0
+
+ for qty, rate in stock_queue:
+ # don't allow negative stock
+ if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
+ continue
+ if qty > 0:
+ self.queue.add_stock(qty, rate)
+ total_qty += qty
+ total_value += qty * rate
+ else:
+ qty = abs(qty)
+ consumed = self.queue.remove_stock(qty)
+ self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}")
+ total_qty -= qty
+ total_value -= sum(q * r for q, r in consumed)
+ self.assertTotalQty(total_qty)
+ self.assertTotalValue(total_value)
diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py
new file mode 100644
index 0000000..45c5083
--- /dev/null
+++ b/erpnext/stock/valuation.py
@@ -0,0 +1,146 @@
+from typing import Callable, List, NewType, Optional, Tuple
+
+from frappe.utils import flt
+
+FifoBin = NewType("FifoBin", List[float])
+
+# Indexes of values inside FIFO bin 2-tuple
+QTY = 0
+RATE = 1
+
+
+class FIFOValuation:
+ """Valuation method where a queue of all the incoming stock is maintained.
+
+ New stock is added at end of the queue.
+ Qty consumption happens on First In First Out basis.
+
+ Queue is implemented using "bins" of [qty, rate].
+
+ ref: https://en.wikipedia.org/wiki/FIFO_and_LIFO_accounting
+ """
+
+ # specifying the attributes to save resources
+ # ref: https://docs.python.org/3/reference/datamodel.html#slots
+ __slots__ = ["queue",]
+
+ def __init__(self, state: Optional[List[FifoBin]]):
+ self.queue: List[FifoBin] = state if state is not None else []
+
+ def __repr__(self):
+ return str(self.queue)
+
+ def __iter__(self):
+ return iter(self.queue)
+
+ def __eq__(self, other):
+ if isinstance(other, list):
+ return self.queue == other
+ return self.queue == other.queue
+
+ def get_state(self) -> List[FifoBin]:
+ """Get current state of queue."""
+ return self.queue
+
+ def get_total_stock_and_value(self) -> Tuple[float, float]:
+ total_qty = 0.0
+ total_value = 0.0
+
+ for qty, rate in self.queue:
+ total_qty += flt(qty)
+ total_value += flt(qty) * flt(rate)
+
+ return _round_off_if_near_zero(total_qty), _round_off_if_near_zero(total_value)
+
+ def add_stock(self, qty: float, rate: float) -> None:
+ """Update fifo queue with new stock.
+
+ args:
+ qty: new quantity to add
+ rate: incoming rate of new quantity"""
+
+ if not len(self.queue):
+ self.queue.append([0, 0])
+
+ # last row has the same rate, merge new bin.
+ if self.queue[-1][RATE] == rate:
+ self.queue[-1][QTY] += qty
+ else:
+ # Item has a positive balance qty, add new entry
+ if self.queue[-1][QTY] > 0:
+ self.queue.append([qty, rate])
+ else: # negative balance qty
+ qty = self.queue[-1][QTY] + qty
+ if qty > 0: # new balance qty is positive
+ self.queue[-1] = [qty, rate]
+ else: # new balance qty is still negative, maintain same rate
+ self.queue[-1][QTY] = qty
+
+ def remove_stock(
+ self, qty: float, outgoing_rate: float = 0.0, rate_generator: Callable[[], float] = None
+ ) -> List[FifoBin]:
+ """Remove stock from the queue and return popped bins.
+
+ args:
+ qty: quantity to remove
+ rate: outgoing rate
+ rate_generator: function to be called if queue is not found and rate is required.
+ """
+ if not rate_generator:
+ rate_generator = lambda : 0.0 # noqa
+
+ consumed_bins = []
+ while qty:
+ if not len(self.queue):
+ # rely on rate generator.
+ self.queue.append([0, rate_generator()])
+
+ index = None
+ if outgoing_rate > 0:
+ # Find the entry where rate matched with outgoing rate
+ for idx, fifo_bin in enumerate(self.queue):
+ if fifo_bin[RATE] == outgoing_rate:
+ index = idx
+ break
+
+ # If no entry found with outgoing rate, collapse queue
+ if index is None: # nosemgrep
+ new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * outgoing_rate
+ new_stock_qty = sum(d[QTY] for d in self.queue) - qty
+ self.queue = [[new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
+ consumed_bins.append([qty, outgoing_rate])
+ break
+ else:
+ index = 0
+
+ # select first bin or the bin with same rate
+ fifo_bin = self.queue[index]
+ if qty >= fifo_bin[QTY]:
+ # consume current bin
+ qty = _round_off_if_near_zero(qty - fifo_bin[QTY])
+ to_consume = self.queue.pop(index)
+ consumed_bins.append(list(to_consume))
+
+ if not self.queue and qty:
+ # stock finished, qty still remains to be withdrawn
+ # negative stock, keep in as a negative bin
+ self.queue.append([-qty, outgoing_rate or fifo_bin[RATE]])
+ consumed_bins.append([qty, outgoing_rate or fifo_bin[RATE]])
+ break
+ else:
+ # qty found in current bin consume it and exit
+ fifo_bin[QTY] = _round_off_if_near_zero(fifo_bin[QTY] - qty)
+ consumed_bins.append([qty, fifo_bin[RATE]])
+ qty = 0
+
+ return consumed_bins
+
+
+def _round_off_if_near_zero(number: float, precision: int = 7) -> float:
+ """Rounds off the number to zero only if number is close to zero for decimal
+ specified in precision. Precision defaults to 7.
+ """
+ if abs(0.0 - flt(number)) < (1.0 / (10 ** precision)):
+ return 0.0
+
+ return flt(number)
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 14cec46..7a0a5e5 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -98,6 +98,7 @@
issue.save()
self.assertEqual(issue.on_hold_since, frappe.flags.current_time)
+ self.assertFalse(issue.resolution_by)
creation = get_datetime("2020-03-04 5:00")
frappe.flags.current_time = get_datetime("2020-03-04 5:00")
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 c94700b..b3348f1 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -476,7 +476,7 @@
priority = get_response_and_resolution_duration(doc)
start_date_time = get_datetime(doc.get("service_level_agreement_creation") or doc.creation)
set_response_by(doc, start_date_time, priority)
- if apply_sla_for_resolution:
+ if apply_sla_for_resolution and not doc.get('on_hold_since'): # resolution_by is reset if on hold
set_resolution_by(doc, start_date_time, priority)
@@ -624,9 +624,6 @@
if doc.meta.has_field("user_resolution_time"):
doc.user_resolution_time = None
- if doc.meta.has_field("agreement_status"):
- doc.agreement_status = "First Response Due"
-
# called via hooks on communication update
def on_communication_update(doc, status):
diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py
index 36a9bf5..6184972 100644
--- a/erpnext/tests/test_init.py
+++ b/erpnext/tests/test_init.py
@@ -8,13 +8,8 @@
class TestInit(unittest.TestCase):
def test_encode_company_abbr(self):
- company = frappe.new_doc("Company")
- company.company_name = "New from Existing Company For Test"
- company.abbr = "NFECT"
- company.default_currency = "INR"
- company.save()
- abbr = company.abbr
+ abbr = "NFECT"
names = [
"Warehouse Name", "ERPNext Foundation India", "Gold - Member - {a}".format(a=abbr),
@@ -32,7 +27,7 @@
]
for i in range(len(names)):
- enc_name = encode_company_abbr(names[i], company.name)
+ enc_name = encode_company_abbr(names[i], abbr=abbr)
self.assertTrue(
enc_name == expected_names[i],
"{enc} is not same as {exp}".format(enc=enc_name, exp=expected_names[i])