Merge branch 'develop' into ordered-qty-for-packed-items

This commit is contained in:
Saqib Ansari
2022-03-02 12:13:56 +05:30
committed by GitHub
85 changed files with 660 additions and 345 deletions

View File

@@ -120,6 +120,7 @@ def get_booking_dates(doc, item, posting_date=None):
prev_gl_entry = frappe.db.sql(''' prev_gl_entry = frappe.db.sql('''
select name, posting_date from `tabGL Entry` where company=%s and account=%s and select name, posting_date from `tabGL Entry` where company=%s and account=%s and
voucher_type=%s and voucher_no=%s and voucher_detail_no=%s voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
order by posting_date desc limit 1 order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
@@ -227,6 +228,7 @@ def get_already_booked_amount(doc, item):
gl_entries_details = frappe.db.sql(''' gl_entries_details = frappe.db.sql('''
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
group by voucher_detail_no group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency), '''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
@@ -282,7 +284,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
return return
# check if books nor frozen till endate: # check if books nor frozen till endate:
if getdate(end_date) >= getdate(accounts_frozen_upto): if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
end_date = get_last_day(add_days(accounts_frozen_upto, 1)) end_date = get_last_day(add_days(accounts_frozen_upto, 1))
if via_journal_entry: if via_journal_entry:

View File

@@ -2,6 +2,7 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import ( from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
create_dimension, create_dimension,
@@ -10,11 +11,10 @@ from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension imp
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import ( from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import (
get_temporary_opening_account, get_temporary_opening_account,
) )
from erpnext.tests.utils import ERPNextTestCase
test_dependencies = ["Customer", "Supplier", "Accounting Dimension"] test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
class TestOpeningInvoiceCreationTool(ERPNextTestCase): class TestOpeningInvoiceCreationTool(FrappeTestCase):
@classmethod @classmethod
def setUpClass(self): def setUpClass(self):
if not frappe.db.exists("Company", "_Test Opening Invoice Company"): if not frappe.db.exists("Company", "_Test Opening Invoice Company"):

View File

@@ -194,8 +194,14 @@ frappe.ui.form.on('Payment Entry', {
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)); frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
(frm.doc.paid_from_account_currency != company_currency)); if (frm.doc.payment_type == "Pay") {
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
(frm.doc.paid_to_account_currency != company_currency));
} else {
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
(frm.doc.paid_from_account_currency != company_currency));
}
frm.toggle_display("base_received_amount", ( frm.toggle_display("base_received_amount", (
frm.doc.paid_to_account_currency != company_currency frm.doc.paid_to_account_currency != company_currency
@@ -230,7 +236,8 @@ frappe.ui.form.on('Payment Entry', {
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount", frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax"], company_currency); "difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax",
"base_total_taxes_and_charges"], company_currency);
frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency); frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency);
frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency); frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency);

View File

@@ -66,7 +66,9 @@
"tax_withholding_category", "tax_withholding_category",
"section_break_56", "section_break_56",
"taxes", "taxes",
"section_break_60",
"base_total_taxes_and_charges", "base_total_taxes_and_charges",
"column_break_61",
"total_taxes_and_charges", "total_taxes_and_charges",
"deductions_or_loss_section", "deductions_or_loss_section",
"deductions", "deductions",
@@ -715,12 +717,21 @@
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
"label": "Paid To Account Type" "label": "Paid To Account Type"
},
{
"fieldname": "column_break_61",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_60",
"fieldtype": "Section Break",
"hide_border": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-11-24 18:58:24.919764", "modified": "2022-02-23 20:08:39.559814",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Entry", "name": "Payment Entry",
@@ -763,6 +774,7 @@
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"title_field": "title", "title_field": "title",
"track_changes": 1 "track_changes": 1
} }

View File

@@ -934,8 +934,12 @@ class PaymentEntry(AccountsController):
tax.base_total = tax.total * self.source_exchange_rate tax.base_total = tax.total * self.source_exchange_rate
self.total_taxes_and_charges += current_tax_amount if self.payment_type == 'Pay':
self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate self.base_total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
else:
self.base_total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
if self.get('taxes'): if self.get('taxes'):
self.paid_amount_after_tax = self.get('taxes')[-1].base_total self.paid_amount_after_tax = self.get('taxes')[-1].base_total

View File

@@ -633,6 +633,45 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(flt(expected_party_balance), party_balance) self.assertEqual(flt(expected_party_balance), party_balance)
self.assertEqual(flt(expected_party_account_balance), party_account_balance) self.assertEqual(flt(expected_party_account_balance), party_account_balance)
def test_multi_currency_payment_entry_with_taxes(self):
payment_entry = create_payment_entry(party='_Test Supplier USD', paid_to = '_Test Payable USD - _TC',
save=True)
payment_entry.append('taxes', {
'account_head': '_Test Account Service Tax - _TC',
'charge_type': 'Actual',
'tax_amount': 10,
'add_deduct_tax': 'Add',
'description': 'Test'
})
payment_entry.save()
self.assertEqual(payment_entry.base_total_taxes_and_charges, 10)
self.assertEqual(flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2))
def create_payment_entry(**args):
payment_entry = frappe.new_doc('Payment Entry')
payment_entry.company = args.get('company') or '_Test Company'
payment_entry.payment_type = args.get('payment_type') or 'Pay'
payment_entry.party_type = args.get('party_type') or 'Supplier'
payment_entry.party = args.get('party') or '_Test Supplier'
payment_entry.paid_from = args.get('paid_from') or '_Test Bank - _TC'
payment_entry.paid_to = args.get('paid_to') or 'Creditors - _TC'
payment_entry.paid_amount = args.get('paid_amount') or 1000
payment_entry.setup_party_account_field()
payment_entry.set_missing_values()
payment_entry.set_exchange_rate()
payment_entry.received_amount = payment_entry.paid_amount / payment_entry.target_exchange_rate
payment_entry.reference_no = 'Test001'
payment_entry.reference_date = nowdate()
if args.get('save'):
payment_entry.save()
if args.get('submit'):
payment_entry.submit()
return payment_entry
def create_payment_terms_template(): def create_payment_terms_template():
create_payment_term('Basic Amount Receivable') create_payment_term('Basic Amount Receivable')

View File

@@ -73,7 +73,7 @@ def get_report_pdf(doc, consolidated=True):
'to_date': doc.to_date, 'to_date': doc.to_date,
'company': doc.company, 'company': doc.company,
'finance_book': doc.finance_book if doc.finance_book else None, 'finance_book': doc.finance_book if doc.finance_book else None,
'account': doc.account if doc.account else None, 'account': [doc.account] if doc.account else None,
'party_type': 'Customer', 'party_type': 'Customer',
'party': [entry.customer], 'party': [entry.customer],
'presentation_currency': presentation_currency, 'presentation_currency': presentation_currency,

View File

@@ -1595,6 +1595,56 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
def test_rounding_adjustment_3(self):
si = create_sales_invoice(do_not_save=True)
si.items = []
for d in [(1122, 2), (1122.01, 1), (1122.01, 1)]:
si.append("items", {
"item_code": "_Test Item",
"gst_hsn_code": "999800",
"warehouse": "_Test Warehouse - _TC",
"qty": d[1],
"rate": d[0],
"income_account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC"
})
for tax_account in ["_Test Account VAT - _TC", "_Test Account Service Tax - _TC"]:
si.append("taxes", {
"charge_type": "On Net Total",
"account_head": tax_account,
"description": tax_account,
"rate": 6,
"cost_center": "_Test Cost Center - _TC",
"included_in_print_rate": 1
})
si.save()
si.submit()
self.assertEqual(si.net_total, 4007.16)
self.assertEqual(si.grand_total, 4488.02)
self.assertEqual(si.total_taxes_and_charges, 480.86)
self.assertEqual(si.rounding_adjustment, -0.02)
expected_values = dict((d[0], d) for d in [
[si.debit_to, 4488.0, 0.0],
["_Test Account Service Tax - _TC", 0.0, 240.43],
["_Test Account VAT - _TC", 0.0, 240.43],
["Sales - _TC", 0.0, 4007.15],
["Round Off - _TC", 0.01, 0]
])
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", si.name, as_dict=1)
debit_credit_diff = 0
for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit)
debit_credit_diff += (gle.debit - gle.credit)
self.assertEqual(debit_credit_diff, 0)
def test_sales_invoice_with_shipping_rule(self): def test_sales_invoice_with_shipping_rule(self):
from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule
@@ -2429,14 +2479,22 @@ class TestSalesInvoice(unittest.TestCase):
def test_sales_commission(self): def test_sales_commission(self):
si = frappe.copy_doc(test_records[0]) si = frappe.copy_doc(test_records[2])
frappe.db.set_value('Item', si.get('items')[0].item_code, 'grant_commission', 1)
frappe.db.set_value('Item', si.get('items')[1].item_code, 'grant_commission', 0)
item = copy.deepcopy(si.get('items')[0]) item = copy.deepcopy(si.get('items')[0])
item.update({ item.update({
"qty": 1, "qty": 1,
"rate": 500, "rate": 500,
"grant_commission": 1
}) })
si.append("items", item)
item = copy.deepcopy(si.get('items')[1])
item.update({
"qty": 1,
"rate": 500,
})
# Test valid values # Test valid values
for commission_rate, total_commission in ((0, 0), (10, 50), (100, 500)): for commission_rate, total_commission in ((0, 0), (10, 50), (100, 500)):

View File

@@ -832,6 +832,7 @@
}, },
{ {
"default": "0", "default": "0",
"fetch_from": "item_code.grant_commission",
"fieldname": "grant_commission", "fieldname": "grant_commission",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Grant Commission", "label": "Grant Commission",
@@ -841,7 +842,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2021-10-05 12:24:54.968907", "modified": "2022-02-24 14:41:36.392560",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",
@@ -849,5 +850,6 @@
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC",
"states": []
} }

View File

@@ -55,5 +55,8 @@ def validate_disabled(doc):
frappe.throw(_("Disabled template must not be default template")) frappe.throw(_("Disabled template must not be default template"))
def validate_for_tax_category(doc): def validate_for_tax_category(doc):
if not doc.tax_category:
return
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0, "name": ["!=", doc.name]}): if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0, "name": ["!=", doc.name]}):
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category))) frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))

View File

@@ -274,7 +274,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
debit_credit_diff += flt(d.credit) debit_credit_diff += flt(d.credit)
round_off_account_exists = True round_off_account_exists = True
if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)): if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
gl_map.remove(round_off_gle) gl_map.remove(round_off_gle)
return return

View File

@@ -3,9 +3,9 @@
import json import json
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, flt, getdate, nowdate from frappe.utils import add_days, flt, getdate, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -27,7 +27,7 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
class TestPurchaseOrder(unittest.TestCase): class TestPurchaseOrder(FrappeTestCase):
def test_make_purchase_receipt(self): def test_make_purchase_receipt(self):
po = create_purchase_order(do_not_submit=True) po = create_purchase_order(do_not_submit=True)
self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name) self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)

View File

@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import ( from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
@@ -16,7 +16,7 @@ from erpnext.stock.doctype.item.test_item import make_item
from erpnext.templates.pages.rfq import check_supplier_has_docname_access from erpnext.templates.pages.rfq import check_supplier_has_docname_access
class TestRequestforQuotation(unittest.TestCase): class TestRequestforQuotation(FrappeTestCase):
def test_quote_status(self): def test_quote_status(self):
rfq = make_request_for_quotation() rfq = make_request_for_quotation()

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import unittest
import frappe import frappe
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
@@ -12,8 +11,10 @@ from erpnext.exceptions import PartyDisabled
test_dependencies = ['Payment Term', 'Payment Terms Template'] test_dependencies = ['Payment Term', 'Payment Terms Template']
test_records = frappe.get_test_records('Supplier') test_records = frappe.get_test_records('Supplier')
from frappe.tests.utils import FrappeTestCase
class TestSupplier(unittest.TestCase):
class TestSupplier(FrappeTestCase):
def test_get_supplier_group_details(self): def test_get_supplier_group_details(self):
doc = frappe.new_doc("Supplier Group") doc = frappe.new_doc("Supplier Group")
doc.supplier_group_name = "_Testing Supplier Group" doc.supplier_group_name = "_Testing Supplier Group"

View File

@@ -3,12 +3,12 @@
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
class TestPurchaseOrder(unittest.TestCase): class TestPurchaseOrder(FrappeTestCase):
def test_make_purchase_order(self): def test_make_purchase_order(self):
from erpnext.buying.doctype.supplier_quotation.supplier_quotation import make_purchase_order from erpnext.buying.doctype.supplier_quotation.supplier_quotation import make_purchase_order

View File

@@ -1,12 +1,12 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
class TestSupplierScorecard(unittest.TestCase): class TestSupplierScorecard(FrappeTestCase):
def test_create_scorecard(self): def test_create_scorecard(self):
doc = make_supplier_scorecard().insert() doc = make_supplier_scorecard().insert()

View File

@@ -1,12 +1,12 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
class TestSupplierScorecardCriteria(unittest.TestCase): class TestSupplierScorecardCriteria(FrappeTestCase):
def test_variables_exist(self): def test_variables_exist(self):
delete_test_scorecards() delete_test_scorecards()
for d in test_good_criteria: for d in test_good_criteria:

View File

@@ -1,16 +1,16 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import ( from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import (
VariablePathNotFound, VariablePathNotFound,
) )
class TestSupplierScorecardVariable(unittest.TestCase): class TestSupplierScorecardVariable(FrappeTestCase):
def test_variable_exist(self): def test_variable_exist(self):
for d in test_existing_variables: for d in test_existing_variables:
my_doc = frappe.get_doc("Supplier Scorecard Variable", d.get("name")) my_doc = frappe.get_doc("Supplier Scorecard Variable", d.get("name"))

View File

@@ -2,10 +2,10 @@
# For license information, please see license.txt # For license information, please see license.txt
import unittest
from datetime import datetime from datetime import datetime
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
from erpnext.buying.report.procurement_tracker.procurement_tracker import execute from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
@@ -14,7 +14,7 @@ from erpnext.stock.doctype.material_request.test_material_request import make_ma
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
class TestProcurementTracker(unittest.TestCase): class TestProcurementTracker(FrappeTestCase):
def test_result_for_procurement_tracker(self): def test_result_for_procurement_tracker(self):
filters = { filters = {
'company': '_Test Procurement Company', 'company': '_Test Procurement Company',

View File

@@ -3,9 +3,9 @@
# Compiled at: 2019-05-06 09:51:46 # Compiled at: 2019-05-06 09:51:46
# Decompiled by https://python-decompiler.com # Decompiled by https://python-decompiler.com
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -15,7 +15,7 @@ from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
class TestSubcontractedItemToBeReceived(unittest.TestCase): class TestSubcontractedItemToBeReceived(FrappeTestCase):
def test_pending_and_received_qty(self): def test_pending_and_received_qty(self):
po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes') po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')

View File

@@ -4,9 +4,9 @@
# Decompiled by https://python-decompiler.com # Decompiled by https://python-decompiler.com
import json import json
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -16,7 +16,7 @@ from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcont
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
class TestSubcontractedItemToBeTransferred(unittest.TestCase): class TestSubcontractedItemToBeTransferred(FrappeTestCase):
def test_pending_and_transferred_qty(self): def test_pending_and_transferred_qty(self):
po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes', supplier_warehouse="_Test Warehouse 1 - _TC") po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes', supplier_warehouse="_Test Warehouse 1 - _TC")

View File

@@ -363,8 +363,6 @@ class Subcontracting():
return return
for row in self.get(self.raw_material_table): for row in self.get(self.raw_material_table):
self.__validate_consumed_qty(row)
key = (row.rm_item_code, row.main_item_code, row.purchase_order) key = (row.rm_item_code, row.main_item_code, row.purchase_order)
if not self.__transferred_items or not self.__transferred_items.get(key): if not self.__transferred_items or not self.__transferred_items.get(key):
return return
@@ -372,12 +370,6 @@ class Subcontracting():
self.__validate_batch_no(row, key) self.__validate_batch_no(row, key)
self.__validate_serial_no(row, key) self.__validate_serial_no(row, key)
def __validate_consumed_qty(self, row):
if self.backflush_based_on != 'BOM' and flt(row.consumed_qty) == 0.0:
msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}'
frappe.throw(_(msg),title=_('Consumed Items Qty Check'))
def __validate_batch_no(self, row, key): def __validate_batch_no(self, row, key):
if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'): if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'):
link = get_link_to_form('Purchase Order', row.purchase_order) link = get_link_to_form('Purchase Order', row.purchase_order)

View File

@@ -5,6 +5,7 @@
import unittest import unittest
import frappe import frappe
from frappe.tests.utils import change_settings
from frappe.utils import add_months, nowdate from frappe.utils import add_months, nowdate
from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule
@@ -15,7 +16,7 @@ from erpnext.e_commerce.shopping_cart.cart import (
get_party, get_party,
update_cart, update_cart,
) )
from erpnext.tests.utils import change_settings, create_test_contact_and_address from erpnext.tests.utils import create_test_contact_and_address
# test_dependencies = ['Payment Terms Template'] # test_dependencies = ['Payment Terms Template']

View File

@@ -1,4 +1,5 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.controllers.item_variant import create_variant from erpnext.controllers.item_variant import create_variant
from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import ( from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import (
@@ -7,11 +8,10 @@ from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings imp
from erpnext.e_commerce.doctype.website_item.website_item import make_website_item from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
from erpnext.e_commerce.variant_selector.utils import get_next_attribute_and_values from erpnext.e_commerce.variant_selector.utils import get_next_attribute_and_values
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.tests.utils import ERPNextTestCase
test_dependencies = ["Item"] test_dependencies = ["Item"]
class TestVariantSelector(ERPNextTestCase): class TestVariantSelector(FrappeTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@@ -116,4 +116,4 @@ class TestVariantSelector(ERPNextTestCase):
self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R") self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R")
self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R") self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R")
self.assertEqual(price_info["price_list_rate"], 100.0) self.assertEqual(price_info["price_list_rate"], 100.0)
self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00") self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00")

View File

@@ -2,7 +2,7 @@
"actions": [], "actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2017-10-09 14:26:29.612365", "creation": "2022-01-17 18:36:51.450395",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
@@ -121,7 +121,7 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"no_copy": 1, "no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled", "options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled",
"read_only": 1 "read_only": 1
}, },
{ {
@@ -200,7 +200,7 @@
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-09-11 18:38:38.617478", "modified": "2022-01-17 19:33:52.345823",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee Advance", "name": "Employee Advance",
@@ -237,5 +237,41 @@
"search_fields": "employee,employee_name", "search_fields": "employee,employee_name",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [
{
"color": "Red",
"custom": 1,
"title": "Draft"
},
{
"color": "Green",
"custom": 1,
"title": "Paid"
},
{
"color": "Orange",
"custom": 1,
"title": "Unpaid"
},
{
"color": "Blue",
"custom": 1,
"title": "Claimed"
},
{
"color": "Gray",
"title": "Returned"
},
{
"color": "Yellow",
"title": "Partly Claimed and Returned"
},
{
"color": "Red",
"custom": 1,
"title": "Cancelled"
}
],
"title_field": "employee_name",
"track_changes": 1 "track_changes": 1
} }

View File

@@ -27,19 +27,33 @@ class EmployeeAdvance(Document):
def on_cancel(self): def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry') self.ignore_linked_doctypes = ('GL Entry')
self.set_status(update=True)
def set_status(self, update=False):
precision = self.precision("paid_amount")
total_amount = flt(flt(self.claimed_amount) + flt(self.return_amount), precision)
status = None
def set_status(self):
if self.docstatus == 0: if self.docstatus == 0:
self.status = "Draft" status = "Draft"
if self.docstatus == 1: elif self.docstatus == 1:
if self.claimed_amount and flt(self.claimed_amount) == flt(self.paid_amount): if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt(self.paid_amount, precision):
self.status = "Claimed" status = "Claimed"
elif self.paid_amount and self.advance_amount == flt(self.paid_amount): elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt(self.paid_amount, precision):
self.status = "Paid" status = "Returned"
elif flt(self.claimed_amount) > 0 and (flt(self.return_amount) > 0) and total_amount == flt(self.paid_amount, precision):
status = "Partly Claimed and Returned"
elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(self.paid_amount, precision):
status = "Paid"
else: else:
self.status = "Unpaid" status = "Unpaid"
elif self.docstatus == 2: elif self.docstatus == 2:
self.status = "Cancelled" status = "Cancelled"
if update:
self.db_set("status", status)
else:
self.status = status
def set_total_advance_paid(self): def set_total_advance_paid(self):
gle = frappe.qb.DocType("GL Entry") gle = frappe.qb.DocType("GL Entry")
@@ -85,9 +99,7 @@ class EmployeeAdvance(Document):
self.db_set("paid_amount", paid_amount) self.db_set("paid_amount", paid_amount)
self.db_set("return_amount", return_amount) self.db_set("return_amount", return_amount)
self.set_status() self.set_status(update=True)
frappe.db.set_value("Employee Advance", self.name , "status", self.status)
def update_claimed_amount(self): def update_claimed_amount(self):
claimed_amount = frappe.db.sql(""" claimed_amount = frappe.db.sql("""
@@ -103,8 +115,8 @@ class EmployeeAdvance(Document):
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount)) frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
self.reload() self.reload()
self.set_status() self.set_status(update=True)
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
@frappe.whitelist() @frappe.whitelist()
def get_pending_amount(employee, posting_date): def get_pending_amount(employee, posting_date):
@@ -222,7 +234,8 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
'reference_name': employee_advance_name, 'reference_name': employee_advance_name,
'party_type': 'Employee', 'party_type': 'Employee',
'party': employee, 'party': employee,
'is_advance': 'Yes' 'is_advance': 'Yes',
'cost_center': erpnext.get_default_cost_center(company)
}) })
bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \ bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \
@@ -233,7 +246,8 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
"debit_in_account_currency": bank_amount, "debit_in_account_currency": bank_amount,
"account_currency": bank_cash_account.account_currency, "account_currency": bank_cash_account.account_currency,
"account_type": bank_cash_account.account_type, "account_type": bank_cash_account.account_type,
"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1 "exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
"cost_center": erpnext.get_default_cost_center(company)
}) })
return je.as_dict() return je.as_dict()

View File

@@ -4,7 +4,7 @@
import unittest import unittest
import frappe import frappe
from frappe.utils import nowdate from frappe.utils import flt, nowdate
import erpnext import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.hr.doctype.employee.test_employee import make_employee
@@ -12,12 +12,21 @@ from erpnext.hr.doctype.employee_advance.employee_advance import (
EmployeeAdvanceOverPayment, EmployeeAdvanceOverPayment,
create_return_through_additional_salary, create_return_through_additional_salary,
make_bank_entry, make_bank_entry,
make_return_entry,
)
from erpnext.hr.doctype.expense_claim.expense_claim import get_advances
from erpnext.hr.doctype.expense_claim.test_expense_claim import (
get_payable_account,
make_expense_claim,
) )
from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
class TestEmployeeAdvance(unittest.TestCase): class TestEmployeeAdvance(unittest.TestCase):
def setUp(self):
frappe.db.delete("Employee Advance")
def test_paid_amount_and_status(self): def test_paid_amount_and_status(self):
employee_name = make_employee("_T@employe.advance") employee_name = make_employee("_T@employe.advance")
advance = make_employee_advance(employee_name) advance = make_employee_advance(employee_name)
@@ -52,9 +61,102 @@ class TestEmployeeAdvance(unittest.TestCase):
self.assertEqual(advance.paid_amount, 0) self.assertEqual(advance.paid_amount, 0)
self.assertEqual(advance.status, "Unpaid") self.assertEqual(advance.status, "Unpaid")
advance.cancel()
advance.reload()
self.assertEqual(advance.status, "Cancelled")
def test_claimed_status(self):
# CLAIMED Status check, full amount claimed
payable_account = get_payable_account("_Test Company")
claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
advance = make_employee_advance(claim.employee)
pe = make_payment_entry(advance)
pe.submit()
claim = get_advances_for_claim(claim, advance.name)
claim.save()
claim.submit()
advance.reload()
self.assertEqual(advance.claimed_amount, 1000)
self.assertEqual(advance.status, "Claimed")
# advance should not be shown in claims
advances = get_advances(claim.employee)
advances = [entry.name for entry in advances]
self.assertTrue(advance.name not in advances)
# cancel claim; status should be Paid
claim.cancel()
advance.reload()
self.assertEqual(advance.claimed_amount, 0)
self.assertEqual(advance.status, "Paid")
def test_partly_claimed_and_returned_status(self):
payable_account = get_payable_account("_Test Company")
claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
advance = make_employee_advance(claim.employee)
pe = make_payment_entry(advance)
pe.submit()
# PARTLY CLAIMED AND RETURNED status check
# 500 Claimed, 500 Returned
claim = make_expense_claim(payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
advance = make_employee_advance(claim.employee)
pe = make_payment_entry(advance)
pe.submit()
claim = get_advances_for_claim(claim, advance.name, amount=500)
claim.save()
claim.submit()
advance.reload()
self.assertEqual(advance.claimed_amount, 500)
self.assertEqual(advance.status, "Paid")
entry = make_return_entry(
employee=advance.employee,
company=advance.company,
employee_advance_name=advance.name,
return_amount=flt(advance.paid_amount - advance.claimed_amount),
advance_account=advance.advance_account,
mode_of_payment=advance.mode_of_payment,
currency=advance.currency,
exchange_rate=advance.exchange_rate
)
entry = frappe.get_doc(entry)
entry.insert()
entry.submit()
advance.reload()
self.assertEqual(advance.return_amount, 500)
self.assertEqual(advance.status, "Partly Claimed and Returned")
# advance should not be shown in claims
advances = get_advances(claim.employee)
advances = [entry.name for entry in advances]
self.assertTrue(advance.name not in advances)
# Cancel return entry; status should change to PAID
entry.cancel()
advance.reload()
self.assertEqual(advance.return_amount, 0)
self.assertEqual(advance.status, "Paid")
# advance should be shown in claims
advances = get_advances(claim.employee)
advances = [entry.name for entry in advances]
self.assertTrue(advance.name in advances)
def test_repay_unclaimed_amount_from_salary(self): def test_repay_unclaimed_amount_from_salary(self):
employee_name = make_employee("_T@employe.advance") employee_name = make_employee("_T@employe.advance")
advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1}) advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1})
pe = make_payment_entry(advance)
pe.submit()
args = {"type": "Deduction"} args = {"type": "Deduction"}
create_salary_component("Advance Salary - Deduction", **args) create_salary_component("Advance Salary - Deduction", **args)
@@ -82,11 +184,13 @@ class TestEmployeeAdvance(unittest.TestCase):
advance.reload() advance.reload()
self.assertEqual(advance.return_amount, 1000) self.assertEqual(advance.return_amount, 1000)
self.assertEqual(advance.status, "Returned")
# update advance return amount on additional salary cancellation # update advance return amount on additional salary cancellation
additional_salary.cancel() additional_salary.cancel()
advance.reload() advance.reload()
self.assertEqual(advance.return_amount, 700) self.assertEqual(advance.return_amount, 700)
self.assertEqual(advance.status, "Paid")
def tearDown(self): def tearDown(self):
frappe.db.rollback() frappe.db.rollback()
@@ -118,3 +222,24 @@ def make_employee_advance(employee_name, args=None):
doc.submit() doc.submit()
return doc return doc
def get_advances_for_claim(claim, advance_name, amount=None):
advances = get_advances(claim.employee, advance_name)
for entry in advances:
if amount:
allocated_amount = amount
else:
allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount)
claim.append("advances", {
"employee_advance": entry.name,
"posting_date": entry.posting_date,
"advance_account": entry.advance_account,
"advance_paid": entry.paid_amount,
"unclaimed_amount": allocated_amount,
"allocated_amount": allocated_amount
})
return claim

View File

@@ -171,7 +171,7 @@ frappe.ui.form.on("Expense Claim", {
['docstatus', '=', 1], ['docstatus', '=', 1],
['employee', '=', frm.doc.employee], ['employee', '=', frm.doc.employee],
['paid_amount', '>', 0], ['paid_amount', '>', 0],
['status', '!=', 'Claimed'] ['status', 'not in', ['Claimed', 'Returned', 'Partly Claimed and Returned']]
] ]
}; };
}); });

View File

@@ -23,10 +23,10 @@ class ExpenseClaim(AccountsController):
def validate(self): def validate(self):
validate_active_employee(self.employee) validate_active_employee(self.employee)
self.validate_advances() set_employee_name(self)
self.validate_sanctioned_amount() self.validate_sanctioned_amount()
self.calculate_total_amount() self.calculate_total_amount()
set_employee_name(self) self.validate_advances()
self.set_expense_account(validate=True) self.set_expense_account(validate=True)
self.set_payable_account() self.set_payable_account()
self.set_cost_center() self.set_cost_center()
@@ -341,18 +341,27 @@ def get_expense_claim_account(expense_claim_type, company):
@frappe.whitelist() @frappe.whitelist()
def get_advances(employee, advance_id=None): def get_advances(employee, advance_id=None):
if not advance_id: advance = frappe.qb.DocType("Employee Advance")
condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount + return_amount'.format(frappe.db.escape(employee))
else:
condition = 'name={0}'.format(frappe.db.escape(advance_id))
return frappe.db.sql(""" query = (
select frappe.qb.from_(advance)
name, posting_date, paid_amount, claimed_amount, advance_account .select(
from advance.name, advance.posting_date, advance.paid_amount,
`tabEmployee Advance` advance.claimed_amount, advance.advance_account
where {0} )
""".format(condition), as_dict=1) )
if not advance_id:
query = query.where(
(advance.docstatus == 1)
& (advance.employee == employee)
& (advance.paid_amount > 0)
& (advance.status.notin(["Claimed", "Returned", "Partly Claimed and Returned"]))
)
else:
query = query.where(advance.name == advance_id)
return query.run(as_dict=True)
@frappe.whitelist() @frappe.whitelist()

View File

@@ -1,15 +1,15 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_months, today from frappe.utils import add_months, today
from erpnext import get_company_currency from erpnext import get_company_currency
from erpnext.tests.utils import ERPNextTestCase
from .blanket_order import make_order from .blanket_order import make_order
class TestBlanketOrder(ERPNextTestCase): class TestBlanketOrder(FrappeTestCase):
def setUp(self): def setUp(self):
frappe.flags.args = frappe._dict() frappe.flags.args = frappe._dict()

View File

@@ -918,7 +918,7 @@ def validate_bom_no(item, bom_no):
frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item)) frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
@frappe.whitelist() @frappe.whitelist()
def get_children(doctype, parent=None, is_root=False, **filters): def get_children(parent=None, is_root=False, **filters):
if not parent or parent=="BOM": if not parent or parent=="BOM":
frappe.msgprint(_('Please select a BOM')) frappe.msgprint(_('Please select a BOM'))
return return

View File

@@ -7,6 +7,7 @@ from functools import partial
import frappe import frappe
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from frappe.utils import cstr, flt from frappe.utils import cstr, flt
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -17,11 +18,10 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
create_stock_reconciliation, create_stock_reconciliation,
) )
from erpnext.tests.test_subcontracting import set_backflush_based_on from erpnext.tests.test_subcontracting import set_backflush_based_on
from erpnext.tests.utils import ERPNextTestCase
test_records = frappe.get_test_records('BOM') test_records = frappe.get_test_records('BOM')
class TestBOM(ERPNextTestCase): class TestBOM(FrappeTestCase):
def setUp(self): def setUp(self):
if not frappe.get_value('Item', '_Test Item'): if not frappe.get_value('Item', '_Test Item'):
make_test_records('Item') make_test_records('Item')

View File

@@ -2,15 +2,15 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
from erpnext.tests.utils import ERPNextTestCase
test_records = frappe.get_test_records('BOM') test_records = frappe.get_test_records('BOM')
class TestBOMUpdateTool(ERPNextTestCase): class TestBOMUpdateTool(FrappeTestCase):
def test_replace_bom(self): def test_replace_bom(self):
current_bom = "BOM-_Test Item Home Desktop Manufactured-001" current_bom = "BOM-_Test Item Home Desktop Manufactured-001"

View File

@@ -2,6 +2,7 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import random_string from frappe.utils import random_string
from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError, OverlapError from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError, OverlapError
@@ -11,10 +12,9 @@ from erpnext.manufacturing.doctype.job_card.job_card import (
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.tests.utils import ERPNextTestCase
class TestJobCard(ERPNextTestCase): class TestJobCard(FrappeTestCase):
def setUp(self): def setUp(self):
make_bom_for_jc_tests() make_bom_for_jc_tests()

View File

@@ -49,7 +49,7 @@ frappe.ui.form.on('Production Plan', {
if (d.item_code) { if (d.item_code) {
return { return {
query: "erpnext.controllers.queries.bom", query: "erpnext.controllers.queries.bom",
filters:{'item': cstr(d.item_code)} filters:{'item': cstr(d.item_code), 'docstatus': 1}
} }
} else frappe.msgprint(__("Please enter Item first")); } else frappe.msgprint(__("Please enter Item first"));
} }
@@ -232,7 +232,7 @@ frappe.ui.form.on('Production Plan', {
}); });
}, },
combine_items: function (frm) { combine_items: function (frm) {
frm.clear_table('prod_plan_references'); frm.clear_table("prod_plan_references");
frappe.call({ frappe.call({
method: "get_items", method: "get_items",
@@ -247,6 +247,13 @@ frappe.ui.form.on('Production Plan', {
}); });
}, },
combine_sub_items: (frm) => {
if (frm.doc.sub_assembly_items.length > 0) {
frm.clear_table("sub_assembly_items");
frm.trigger("get_sub_assembly_items");
}
},
get_sub_assembly_items: function(frm) { get_sub_assembly_items: function(frm) {
frm.dirty(); frm.dirty();

View File

@@ -36,6 +36,7 @@
"prod_plan_references", "prod_plan_references",
"section_break_24", "section_break_24",
"get_sub_assembly_items", "get_sub_assembly_items",
"combine_sub_items",
"sub_assembly_items", "sub_assembly_items",
"material_request_planning", "material_request_planning",
"include_non_stock_items", "include_non_stock_items",
@@ -340,7 +341,6 @@
{ {
"fieldname": "prod_plan_references", "fieldname": "prod_plan_references",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 1,
"label": "Production Plan Item Reference", "label": "Production Plan Item Reference",
"options": "Production Plan Item Reference" "options": "Production Plan Item Reference"
}, },
@@ -370,16 +370,23 @@
"fieldname": "to_delivery_date", "fieldname": "to_delivery_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "To Delivery Date" "label": "To Delivery Date"
},
{
"default": "0",
"fieldname": "combine_sub_items",
"fieldtype": "Check",
"label": "Consolidate Sub Assembly Items"
} }
], ],
"icon": "fa fa-calendar", "icon": "fa fa-calendar",
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-09-06 18:35:59.642232", "modified": "2022-02-23 17:16:10.629378",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan", "name": "Production Plan",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@@ -21,7 +21,8 @@ from frappe.utils import (
) )
from frappe.utils.csvutils import build_csv_response from frappe.utils.csvutils import build_csv_response
from erpnext.manufacturing.doctype.bom.bom import get_children, validate_bom_no from erpnext.manufacturing.doctype.bom.bom import get_children as get_bom_children
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@@ -570,17 +571,28 @@ class ProductionPlan(Document):
@frappe.whitelist() @frappe.whitelist()
def get_sub_assembly_items(self, manufacturing_type=None): def get_sub_assembly_items(self, manufacturing_type=None):
"Fetch sub assembly items and optionally combine them."
self.sub_assembly_items = [] self.sub_assembly_items = []
sub_assembly_items_store = [] # temporary store to process all subassembly items
for row in self.po_items: for row in self.po_items:
bom_data = [] bom_data = []
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty) get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
sub_assembly_items_store.extend(bom_data)
self.sub_assembly_items.sort(key= lambda d: d.bom_level, reverse=True) if self.combine_sub_items:
for idx, row in enumerate(self.sub_assembly_items, start=1): # Combine subassembly items
row.idx = idx sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
sub_assembly_items_store.sort(key= lambda d: d.bom_level, reverse=True) # sort by bom level
for idx, row in enumerate(sub_assembly_items_store):
row.idx = idx + 1
self.append("sub_assembly_items", row)
def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None): def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
"Modify bom_data, set additional details."
for data in bom_data: for data in bom_data:
data.qty = data.stock_qty data.qty = data.stock_qty
data.production_plan_item = row.name data.production_plan_item = row.name
@@ -589,7 +601,32 @@ class ProductionPlan(Document):
data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
else "In House") else "In House")
self.append("sub_assembly_items", data) def combine_subassembly_items(self, sub_assembly_items_store):
"Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No."
key_wise_data = {}
for row in sub_assembly_items_store:
key = (
row.get("production_item"), row.get("fg_warehouse"),
row.get("bom_no"), row.get("type_of_manufacturing")
)
if key not in key_wise_data:
# intialise (item, wh, bom no, man.g type) wise dict
key_wise_data[key] = row
continue
existing_row = key_wise_data[key]
if existing_row:
# if row with same (item, wh, bom no, man.g type) key, merge
existing_row.qty += flt(row.qty)
existing_row.stock_qty += flt(row.stock_qty)
existing_row.bom_level = max(existing_row.bom_level, row.bom_level)
continue
else:
# add row with key
key_wise_data[key] = row
sub_assembly_items_store = [key_wise_data[key] for key in key_wise_data] # unpack into single level list
return sub_assembly_items_store
def all_items_completed(self): def all_items_completed(self):
all_items_produced = all(flt(d.planned_qty) - flt(d.produced_qty) < 0.000001 all_items_produced = all(flt(d.planned_qty) - flt(d.produced_qty) < 0.000001
@@ -1031,7 +1068,7 @@ def get_item_data(item_code):
} }
def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0): def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
data = get_children('BOM', parent = bom_no) data = get_bom_children(parent=bom_no)
for d in data: for d in data:
if d.expandable: if d.expandable:
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item") parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_to_date, flt, now_datetime, nowdate from frappe.utils import add_to_date, flt, now_datetime, nowdate
from erpnext.controllers.item_variant import create_variant from erpnext.controllers.item_variant import create_variant
@@ -16,10 +17,9 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation, create_stock_reconciliation,
) )
from erpnext.tests.utils import ERPNextTestCase
class TestProductionPlan(ERPNextTestCase): class TestProductionPlan(FrappeTestCase):
def setUp(self): def setUp(self):
for item in ['Test Production Item 1', 'Subassembly Item 1', for item in ['Test Production Item 1', 'Subassembly Item 1',
'Raw Material Item 1', 'Raw Material Item 2']: 'Raw Material Item 1', 'Raw Material Item 2']:
@@ -38,6 +38,9 @@ class TestProductionPlan(ERPNextTestCase):
if not frappe.db.get_value('BOM', {'item': item}): if not frappe.db.get_value('BOM', {'item': item}):
make_bom(item = item, raw_materials = raw_materials) make_bom(item = item, raw_materials = raw_materials)
def tearDown(self) -> None:
frappe.db.rollback()
def test_production_plan_mr_creation(self): def test_production_plan_mr_creation(self):
"Test if MRs are created for unavailable raw materials." "Test if MRs are created for unavailable raw materials."
pln = create_production_plan(item_code='Test Production Item 1') pln = create_production_plan(item_code='Test Production Item 1')
@@ -110,7 +113,7 @@ class TestProductionPlan(ERPNextTestCase):
item_code='Test Production Item 1', item_code='Test Production Item 1',
ignore_existing_ordered_qty=1 ignore_existing_ordered_qty=1
) )
self.assertTrue(len(pln.mr_items), 1) self.assertTrue(len(pln.mr_items))
self.assertTrue(flt(pln.mr_items[0].quantity), 1.0) self.assertTrue(flt(pln.mr_items[0].quantity), 1.0)
sr1.cancel() sr1.cancel()
@@ -151,7 +154,7 @@ class TestProductionPlan(ERPNextTestCase):
use_multi_level_bom=0, use_multi_level_bom=0,
ignore_existing_ordered_qty=0 ignore_existing_ordered_qty=0
) )
self.assertTrue(len(pln.mr_items), 0) self.assertFalse(len(pln.mr_items))
sr1.cancel() sr1.cancel()
sr2.cancel() sr2.cancel()
@@ -258,6 +261,51 @@ class TestProductionPlan(ERPNextTestCase):
pln.reload() pln.reload()
pln.cancel() pln.cancel()
def test_production_plan_combine_subassembly(self):
"""
Test combining Sub assembly items belonging to the same BOM in Prod Plan.
1) Red-Car -> Wheel (sub assembly) > BOM-WHEEL-001
2) Green-Car -> Wheel (sub assembly) > BOM-WHEEL-001
"""
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
bom_tree_1 = {
"Red-Car": {"Wheel": {"Rubber": {}}}
}
bom_tree_2 = {
"Green-Car": {"Wheel": {"Rubber": {}}}
}
parent_bom_1 = create_nested_bom(bom_tree_1, prefix="")
parent_bom_2 = create_nested_bom(bom_tree_2, prefix="")
# make sure both boms use same subassembly bom
subassembly_bom = parent_bom_1.items[0].bom_no
frappe.db.set_value("BOM Item", parent_bom_2.items[0].name, "bom_no", subassembly_bom)
plan = create_production_plan(item_code="Red-Car", use_multi_level_bom=1, do_not_save=True)
plan.append("po_items", { # Add Green-Car to Prod Plan
'use_multi_level_bom': 1,
'item_code': "Green-Car",
'bom_no': frappe.db.get_value('Item', "Green-Car", 'default_bom'),
'planned_qty': 1,
'planned_start_date': now_datetime()
})
plan.get_sub_assembly_items()
self.assertTrue(len(plan.sub_assembly_items), 2)
plan.combine_sub_items = 1
plan.get_sub_assembly_items()
self.assertTrue(len(plan.sub_assembly_items), 1) # check if sub-assembly items merged
self.assertEqual(plan.sub_assembly_items[0].qty, 2.0)
self.assertEqual(plan.sub_assembly_items[0].stock_qty, 2.0)
# change warehouse in one row, sub-assemblies should not merge
plan.po_items[0].warehouse = "Finished Goods - _TC"
plan.get_sub_assembly_items()
self.assertTrue(len(plan.sub_assembly_items), 2)
def test_pp_to_mr_customer_provided(self): def test_pp_to_mr_customer_provided(self):
" Test Material Request from Production Plan for Customer Provided Item." " Test Material Request from Production Plan for Customer Provided Item."
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
@@ -532,6 +580,7 @@ class TestProductionPlan(ERPNextTestCase):
wip_warehouse='Work In Progress - _TC', wip_warehouse='Work In Progress - _TC',
fg_warehouse='Finished Goods - _TC', fg_warehouse='Finished Goods - _TC',
skip_transfer=1, skip_transfer=1,
use_multi_level_bom=1,
do_not_submit=True do_not_submit=True
) )
wo.production_plan = pln.name wo.production_plan = pln.name
@@ -576,6 +625,7 @@ class TestProductionPlan(ERPNextTestCase):
wip_warehouse='Work In Progress - _TC', wip_warehouse='Work In Progress - _TC',
fg_warehouse='Finished Goods - _TC', fg_warehouse='Finished Goods - _TC',
skip_transfer=1, skip_transfer=1,
use_multi_level_bom=1,
do_not_submit=True do_not_submit=True
) )
wo.production_plan = pln.name wo.production_plan = pln.name

View File

@@ -2,14 +2,14 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.tests.utils import ERPNextTestCase
class TestRouting(ERPNextTestCase): class TestRouting(FrappeTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.item_code = "Test Routing Item - A" cls.item_code = "Test Routing Item - A"

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase, timeout
from frappe.utils import add_days, add_months, cint, flt, now, today from frappe.utils import add_days, add_months, cint, flt, now, today
from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
@@ -21,10 +22,9 @@ from erpnext.stock.doctype.item.test_item import create_item, make_item
from erpnext.stock.doctype.stock_entry import test_stock_entry from erpnext.stock.doctype.stock_entry import test_stock_entry
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.utils import get_bin from erpnext.stock.utils import get_bin
from erpnext.tests.utils import ERPNextTestCase, timeout
class TestWorkOrder(ERPNextTestCase): class TestWorkOrder(FrappeTestCase):
def setUp(self): def setUp(self):
self.warehouse = '_Test Warehouse 2 - _TC' self.warehouse = '_Test Warehouse 2 - _TC'
self.item = '_Test Item' self.item = '_Test Item'
@@ -1040,7 +1040,7 @@ def make_wo_order_test_record(**args):
wo_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC" wo_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC"
wo_order.company = args.company or "_Test Company" wo_order.company = args.company or "_Test Company"
wo_order.stock_uom = args.stock_uom or "_Test UOM" wo_order.stock_uom = args.stock_uom or "_Test UOM"
wo_order.use_multi_level_bom=0 wo_order.use_multi_level_bom= args.use_multi_level_bom or 0
wo_order.skip_transfer=args.skip_transfer or 0 wo_order.skip_transfer=args.skip_transfer or 0
wo_order.get_items_and_operations_from_bom() wo_order.get_items_and_operations_from_bom()
wo_order.sales_order = args.sales_order or None wo_order.sales_order = args.sales_order or None

View File

@@ -854,7 +854,7 @@ def get_item_details(item, project = None, skip_bom_info=False):
res = res[0] res = res[0]
if skip_bom_info: return res if skip_bom_info: return res
filters = {"item": item, "is_default": 1} filters = {"item": item, "is_default": 1, "docstatus": 1}
if project: if project:
filters = {"item": item, "project": project} filters = {"item": item, "project": project}

View File

@@ -2,6 +2,7 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from erpnext.manufacturing.doctype.operation.test_operation import make_operation from erpnext.manufacturing.doctype.operation.test_operation import make_operation
from erpnext.manufacturing.doctype.routing.test_routing import create_routing, setup_bom from erpnext.manufacturing.doctype.routing.test_routing import create_routing, setup_bom
@@ -10,13 +11,12 @@ from erpnext.manufacturing.doctype.workstation.workstation import (
WorkstationHolidayError, WorkstationHolidayError,
check_if_within_operating_hours, check_if_within_operating_hours,
) )
from erpnext.tests.utils import ERPNextTestCase
test_dependencies = ["Warehouse"] test_dependencies = ["Warehouse"]
test_records = frappe.get_test_records('Workstation') test_records = frappe.get_test_records('Workstation')
make_test_records('Workstation') make_test_records('Workstation')
class TestWorkstation(ERPNextTestCase): class TestWorkstation(FrappeTestCase):
def test_validate_timings(self): def test_validate_timings(self):
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00") check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00") check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")

View File

@@ -356,4 +356,5 @@ erpnext.patches.v14_0.delete_amazon_mws_doctype
erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr
erpnext.patches.v13_0.update_accounts_in_loan_docs erpnext.patches.v13_0.update_accounts_in_loan_docs
erpnext.patches.v14_0.update_batch_valuation_flag erpnext.patches.v14_0.update_batch_valuation_flag
erpnext.patches.v14_0.delete_non_profit_doctypes erpnext.patches.v14_0.delete_non_profit_doctypes
erpnext.patches.v14_0.update_employee_advance_status

View File

@@ -0,0 +1,26 @@
import frappe
def execute():
frappe.reload_doc('hr', 'doctype', 'employee_advance')
advance = frappe.qb.DocType('Employee Advance')
(frappe.qb
.update(advance)
.set(advance.status, 'Returned')
.where(
(advance.docstatus == 1)
& ((advance.return_amount) & (advance.paid_amount == advance.return_amount))
& (advance.status == 'Paid')
)
).run()
(frappe.qb
.update(advance)
.set(advance.status, 'Partly Claimed and Returned')
.where(
(advance.docstatus == 1)
& ((advance.claimed_amount & advance.return_amount) & (advance.paid_amount == (advance.return_amount + advance.claimed_amount)))
& (advance.status == 'Paid')
)
).run()

View File

@@ -105,6 +105,8 @@ class AdditionalSalary(Document):
return_amount += self.amount return_amount += self.amount
frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount) frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount)
advance = frappe.get_doc("Employee Advance", self.ref_docname)
advance.set_status(update=True)
def update_employee_referral(self, cancel=False): def update_employee_referral(self, cancel=False):
if self.ref_doctype == "Employee Referral": if self.ref_doctype == "Employee Referral":

View File

@@ -4,12 +4,13 @@
import frappe import frappe
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt from frappe.utils import flt
from erpnext.accounts.party import get_due_date from erpnext.accounts.party import get_due_date
from erpnext.exceptions import PartyDisabled, PartyFrozen from erpnext.exceptions import PartyDisabled, PartyFrozen
from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address from erpnext.tests.utils import create_test_contact_and_address
test_ignore = ["Price List"] test_ignore = ["Price List"]
test_dependencies = ['Payment Term', 'Payment Terms Template'] test_dependencies = ['Payment Term', 'Payment Terms Template']
@@ -17,7 +18,7 @@ test_records = frappe.get_test_records('Customer')
class TestCustomer(ERPNextTestCase): class TestCustomer(FrappeTestCase):
def setUp(self): def setUp(self):
if not frappe.get_value('Item', '_Test Item'): if not frappe.get_value('Item', '_Test Item'):
make_test_records('Item') make_test_records('Item')

View File

@@ -1,12 +1,10 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.controllers.queries import item_query from erpnext.controllers.queries import item_query
from erpnext.tests.utils import ERPNextTestCase
test_dependencies = ['Item', 'Customer', 'Supplier'] test_dependencies = ['Item', 'Customer', 'Supplier']
@@ -18,7 +16,7 @@ def create_party_specific_item(**args):
psi.based_on_value = args.get('based_on_value') psi.based_on_value = args.get('based_on_value')
psi.insert() psi.insert()
class TestPartySpecificItem(ERPNextTestCase): class TestPartySpecificItem(FrappeTestCase):
def setUp(self): def setUp(self):
self.customer = frappe.get_last_doc("Customer") self.customer = frappe.get_last_doc("Customer")
self.supplier = frappe.get_last_doc("Supplier") self.supplier = frappe.get_last_doc("Supplier")

View File

@@ -2,14 +2,13 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, add_months, flt, getdate, nowdate from frappe.utils import add_days, add_months, flt, getdate, nowdate
from erpnext.tests.utils import ERPNextTestCase
test_dependencies = ["Product Bundle"] test_dependencies = ["Product Bundle"]
class TestQuotation(ERPNextTestCase): class TestQuotation(FrappeTestCase):
def test_make_quotation_without_terms(self): def test_make_quotation_without_terms(self):
quotation = make_quotation(do_not_save=1) quotation = make_quotation(do_not_save=1)
self.assertFalse(quotation.get('payment_schedule')) self.assertFalse(quotation.get('payment_schedule'))

View File

@@ -6,6 +6,7 @@ import json
import frappe import frappe
import frappe.permissions import frappe.permissions
from frappe.core.doctype.user_permission.test_user_permission import create_user from frappe.core.doctype.user_permission.test_user_permission import create_user
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, flt, getdate, nowdate, today from frappe.utils import add_days, flt, getdate, nowdate, today
from erpnext.controllers.accounts_controller import update_child_qty_rate from erpnext.controllers.accounts_controller import update_child_qty_rate
@@ -27,10 +28,9 @@ from erpnext.selling.doctype.sales_order.sales_order import (
) )
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.tests.utils import ERPNextTestCase
class TestSalesOrder(ERPNextTestCase): class TestSalesOrder(FrappeTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):

View File

@@ -83,8 +83,8 @@
"planned_qty", "planned_qty",
"column_break_69", "column_break_69",
"work_order_qty", "work_order_qty",
"produced_qty",
"delivered_qty", "delivered_qty",
"produced_qty",
"returned_qty", "returned_qty",
"shopping_cart_section", "shopping_cart_section",
"additional_notes", "additional_notes",
@@ -701,8 +701,10 @@
"width": "50px" "width": "50px"
}, },
{ {
"description": "For Production",
"fieldname": "produced_qty", "fieldname": "produced_qty",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 1,
"label": "Produced Quantity", "label": "Produced Quantity",
"oldfieldname": "produced_qty", "oldfieldname": "produced_qty",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
@@ -791,6 +793,7 @@
}, },
{ {
"default": "0", "default": "0",
"fetch_from": "item_code.grant_commission",
"fieldname": "grant_commission", "fieldname": "grant_commission",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Grant Commission", "label": "Grant Commission",
@@ -800,7 +803,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-02-21 13:55:08.883104", "modified": "2022-02-24 14:41:57.325799",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@@ -1,6 +1,7 @@
import datetime import datetime
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days from frappe.utils import add_days
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
@@ -9,12 +10,11 @@ from erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_s
execute, execute,
) )
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
from erpnext.tests.utils import ERPNextTestCase
test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template"] test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template"]
class TestPaymentTermsStatusForSalesOrder(ERPNextTestCase): class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
def create_payment_terms_template(self): def create_payment_terms_template(self):
# create template for 50-50 payments # create template for 50-50 payments
template = None template = None

View File

@@ -2,6 +2,7 @@
# For license information, please see license.txt # For license information, please see license.txt
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_months, nowdate from frappe.utils import add_months, nowdate
from erpnext.selling.doctype.sales_order.sales_order import make_material_request from erpnext.selling.doctype.sales_order.sales_order import make_material_request
@@ -9,10 +10,9 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import ( from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import (
execute, execute,
) )
from erpnext.tests.utils import ERPNextTestCase
class TestPendingSOItemsForPurchaseRequest(ERPNextTestCase): class TestPendingSOItemsForPurchaseRequest(FrappeTestCase):
def test_result_for_partial_material_request(self): def test_result_for_partial_material_request(self):
so = make_sales_order() so = make_sales_order()
mr=make_material_request(so.name) mr=make_material_request(so.name)

View File

@@ -3,13 +3,13 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.selling.report.sales_analytics.sales_analytics import execute from erpnext.selling.report.sales_analytics.sales_analytics import execute
from erpnext.tests.utils import ERPNextTestCase
class TestAnalytics(ERPNextTestCase): class TestAnalytics(FrappeTestCase):
def test_sales_analytics(self): def test_sales_analytics(self):
frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'") frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")

View File

@@ -213,7 +213,14 @@ erpnext.stock.move_item = function (item, source, target, actual_qty, rate, call
label: __('Target Warehouse'), label: __('Target Warehouse'),
fieldtype: 'Link', fieldtype: 'Link',
options: 'Warehouse', options: 'Warehouse',
reqd: 1 reqd: 1,
get_query() {
return {
filters: {
is_group: 0
}
}
}
}, },
{ {
fieldname: 'qty', fieldname: 'qty',
@@ -252,52 +259,21 @@ erpnext.stock.move_item = function (item, source, target, actual_qty, rate, call
dialog.get_field('target').refresh(); dialog.get_field('target').refresh();
} }
dialog.set_primary_action(__('Submit'), function () { dialog.set_primary_action(__('Create Stock Entry'), function () {
var values = dialog.get_values(); frappe.model.with_doctype('Stock Entry', function () {
if (!values) { let doc = frappe.model.get_new_doc('Stock Entry');
return; doc.from_warehouse = dialog.get_value('source');
} doc.to_warehouse = dialog.get_value('target');
if (source && values.qty > actual_qty) { doc.stock_entry_type = doc.from_warehouse ? "Material Transfer" : "Material Receipt";
frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty])); let row = frappe.model.add_child(doc, 'items');
return; row.item_code = dialog.get_value('item_code');
} row.s_warehouse = dialog.get_value('source');
if (values.source === values.target) { row.t_warehouse = dialog.get_value('target');
frappe.msgprint(__('Source and target warehouse must be different')); row.qty = dialog.get_value('qty');
} row.conversion_factor = 1;
row.transfer_qty = dialog.get_value('qty');
frappe.call({ row.basic_rate = dialog.get_value('rate');
method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry', frappe.set_route('Form', doc.doctype, doc.name);
args: values,
btn: dialog.get_primary_btn(),
freeze: true,
freeze_message: __('Creating Stock Entry'),
callback: function (r) {
frappe.show_alert(__('Stock Entry {0} created',
['<a href="/app/stock-entry/' + r.message.name + '">' + r.message.name + '</a>']));
dialog.hide();
callback(r);
},
}); });
}); });
$('<p style="margin-left: 10px;"><a class="link-open text-muted small">' +
__("Add more items or open full form") + '</a></p>')
.appendTo(dialog.body)
.find('.link-open')
.on('click', function () {
frappe.model.with_doctype('Stock Entry', function () {
var doc = frappe.model.get_new_doc('Stock Entry');
doc.from_warehouse = dialog.get_value('source');
doc.to_warehouse = dialog.get_value('target');
var row = frappe.model.add_child(doc, 'items');
row.item_code = dialog.get_value('item_code');
row.f_warehouse = dialog.get_value('target');
row.t_warehouse = dialog.get_value('target');
row.qty = dialog.get_value('qty');
row.conversion_factor = 1;
row.transfer_qty = dialog.get_value('qty');
row.basic_rate = dialog.get_value('rate');
frappe.set_route('Form', doc.doctype, doc.name);
});
});
}; };

View File

@@ -5,6 +5,7 @@ import json
import frappe import frappe
from frappe.exceptions import ValidationError from frappe.exceptions import ValidationError
from frappe.tests.utils import FrappeTestCase
from frappe.utils import cint, flt from frappe.utils import cint, flt
from frappe.utils.data import add_to_date, getdate from frappe.utils.data import add_to_date, getdate
@@ -16,10 +17,9 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
) )
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.tests.utils import ERPNextTestCase
class TestBatch(ERPNextTestCase): class TestBatch(FrappeTestCase):
def test_item_has_batch_enabled(self): def test_item_has_batch_enabled(self):
self.assertRaises(ValidationError, frappe.get_doc({ self.assertRaises(ValidationError, frappe.get_doc({
"doctype": "Batch", "doctype": "Batch",

View File

@@ -2,13 +2,13 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.utils import _create_bin from erpnext.stock.utils import _create_bin
from erpnext.tests.utils import ERPNextTestCase
class TestBin(ERPNextTestCase): class TestBin(FrappeTestCase):
def test_concurrent_inserts(self): def test_concurrent_inserts(self):

View File

@@ -6,6 +6,7 @@
import json import json
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import cstr, flt, nowdate, nowtime from frappe.utils import cstr, flt, nowdate, nowtime
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -35,10 +36,9 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
) )
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.tests.utils import ERPNextTestCase
class TestDeliveryNote(ERPNextTestCase): class TestDeliveryNote(FrappeTestCase):
def test_over_billing_against_dn(self): def test_over_billing_against_dn(self):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)

View File

@@ -757,6 +757,7 @@
}, },
{ {
"default": "0", "default": "0",
"fetch_from": "item_code.grant_commission",
"fieldname": "grant_commission", "fieldname": "grant_commission",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Grant Commission", "label": "Grant Commission",
@@ -767,12 +768,14 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2021-10-06 12:12:44.018872", "modified": "2022-02-24 14:42:20.211085",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note Item", "name": "Delivery Note Item",
"naming_rule": "Random",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC",
} "states": []
}

View File

@@ -4,6 +4,7 @@
import unittest import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, flt, now_datetime, nowdate from frappe.utils import add_days, flt, now_datetime, nowdate
import erpnext import erpnext
@@ -12,10 +13,10 @@ from erpnext.stock.doctype.delivery_trip.delivery_trip import (
make_expense_claim, make_expense_claim,
notify_customers, notify_customers,
) )
from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address from erpnext.tests.utils import create_test_contact_and_address
class TestDeliveryTrip(ERPNextTestCase): class TestDeliveryTrip(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
driver = create_driver() driver = create_driver()

View File

@@ -6,6 +6,7 @@ import json
import frappe import frappe
from frappe.test_runner import make_test_objects from frappe.test_runner import make_test_objects
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, today from frappe.utils import add_days, today
from erpnext.controllers.item_variant import ( from erpnext.controllers.item_variant import (
@@ -25,7 +26,6 @@ from erpnext.stock.doctype.item.item import (
) )
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
from erpnext.tests.utils import ERPNextTestCase, change_settings
test_ignore = ["BOM"] test_ignore = ["BOM"]
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"] test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -53,7 +53,7 @@ def make_item(item_code, properties=None):
return item return item
class TestItem(ERPNextTestCase): class TestItem(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
frappe.flags.attribute_values = None frappe.flags.attribute_values = None

View File

@@ -4,6 +4,7 @@
import json import json
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt from frappe.utils import flt
from erpnext.buying.doctype.purchase_order.purchase_order import ( from erpnext.buying.doctype.purchase_order.purchase_order import (
@@ -18,10 +19,9 @@ from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation, create_stock_reconciliation,
) )
from erpnext.tests.utils import ERPNextTestCase
class TestItemAlternative(ERPNextTestCase): class TestItemAlternative(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
make_items() make_items()

View File

@@ -6,11 +6,12 @@ import frappe
test_records = frappe.get_test_records('Item Attribute') test_records = frappe.get_test_records('Item Attribute')
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
from erpnext.tests.utils import ERPNextTestCase
class TestItemAttribute(ERPNextTestCase): class TestItemAttribute(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
if frappe.db.exists("Item Attribute", "_Test_Length"): if frappe.db.exists("Item Attribute", "_Test_Length"):

View File

@@ -4,13 +4,13 @@
import frappe import frappe
from frappe.test_runner import make_test_records_for_doctype from frappe.test_runner import make_test_records_for_doctype
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
from erpnext.stock.get_item_details import get_price_list_rate_for, process_args from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
from erpnext.tests.utils import ERPNextTestCase
class TestItemPrice(ERPNextTestCase): class TestItemPrice(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
frappe.db.sql("delete from `tabItem Price`") frappe.db.sql("delete from `tabItem Price`")

View File

@@ -4,6 +4,7 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_to_date, flt, now from frappe.utils import add_to_date, flt, now
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
@@ -14,10 +15,9 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import (
get_gl_entries, get_gl_entries,
make_purchase_receipt, make_purchase_receipt,
) )
from erpnext.tests.utils import ERPNextTestCase
class TestLandedCostVoucher(ERPNextTestCase): class TestLandedCostVoucher(FrappeTestCase):
def test_landed_cost_voucher(self): def test_landed_cost_voucher(self):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1) frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)

View File

@@ -6,6 +6,7 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt, today from frappe.utils import flt, today
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
@@ -15,10 +16,9 @@ from erpnext.stock.doctype.material_request.material_request import (
make_supplier_quotation, make_supplier_quotation,
raise_work_orders, raise_work_orders,
) )
from erpnext.tests.utils import ERPNextTestCase
class TestMaterialRequest(ERPNextTestCase): class TestMaterialRequest(FrappeTestCase):
def test_make_purchase_order(self): def test_make_purchase_order(self):
mr = frappe.copy_doc(test_records[0]).insert() mr = frappe.copy_doc(test_records[0]).insert()

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_to_date, nowdate from frappe.utils import add_to_date, nowdate
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
@@ -9,10 +10,9 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.tests.utils import ERPNextTestCase, change_settings
class TestPackedItem(ERPNextTestCase): class TestPackedItem(FrappeTestCase):
"Test impact on Packed Items table in various scenarios." "Test impact on Packed Items table in various scenarios."
@classmethod @classmethod
def setUpClass(cls) -> None: def setUpClass(cls) -> None:

View File

@@ -4,7 +4,7 @@
import unittest import unittest
# test_records = frappe.get_test_records('Packing Slip') # test_records = frappe.get_test_records('Packing Slip')
from erpnext.tests.utils import ERPNextTestCase from frappe.tests.utils import FrappeTestCase
class TestPackingSlip(unittest.TestCase): class TestPackingSlip(unittest.TestCase):

View File

@@ -6,16 +6,17 @@ from frappe import _dict
test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import ( from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
EmptyStockReconciliationItemsError, EmptyStockReconciliationItemsError,
) )
from erpnext.tests.utils import ERPNextTestCase
class TestPickList(ERPNextTestCase): class TestPickList(FrappeTestCase):
def test_pick_list_picks_warehouse_for_each_item(self): def test_pick_list_picks_warehouse_for_each_item(self):
try: try:

View File

@@ -7,6 +7,7 @@ import unittest
from collections import defaultdict from collections import defaultdict
import frappe import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, cint, cstr, flt, today from frappe.utils import add_days, cint, cstr, flt, today
import erpnext import erpnext
@@ -17,10 +18,9 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchas
from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
from erpnext.tests.utils import ERPNextTestCase, change_settings
class TestPurchaseReceipt(ERPNextTestCase): class TestPurchaseReceipt(FrappeTestCase):
def setUp(self): def setUp(self):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1) frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)

View File

@@ -2,6 +2,7 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.batch.test_batch import make_new_batch from erpnext.stock.doctype.batch.test_batch import make_new_batch
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
@@ -9,10 +10,9 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.tests.utils import ERPNextTestCase
class TestPutawayRule(ERPNextTestCase): class TestPutawayRule(FrappeTestCase):
def setUp(self): def setUp(self):
if not frappe.db.exists("Item", "_Rice"): if not frappe.db.exists("Item", "_Rice"):
make_item("_Rice", { make_item("_Rice", {

View File

@@ -2,6 +2,7 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.controllers.stock_controller import ( from erpnext.controllers.stock_controller import (
@@ -13,12 +14,11 @@ from erpnext.controllers.stock_controller import (
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.tests.utils import ERPNextTestCase
# test_records = frappe.get_test_records('Quality Inspection') # test_records = frappe.get_test_records('Quality Inspection')
class TestQualityInspection(ERPNextTestCase): class TestQualityInspection(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
create_item("_Test Item with QA") create_item("_Test Item with QA")

View File

@@ -18,11 +18,12 @@ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
test_dependencies = ["Item"] test_dependencies = ["Item"]
test_records = frappe.get_test_records('Serial No') test_records = frappe.get_test_records('Serial No')
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.serial_no.serial_no import * from erpnext.stock.doctype.serial_no.serial_no import *
from erpnext.tests.utils import ERPNextTestCase
class TestSerialNo(ERPNextTestCase): class TestSerialNo(FrappeTestCase):
def tearDown(self): def tearDown(self):
frappe.db.rollback() frappe.db.rollback()

View File

@@ -4,12 +4,12 @@
from datetime import date, timedelta from datetime import date, timedelta
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
from erpnext.tests.utils import ERPNextTestCase
class TestShipment(ERPNextTestCase): class TestShipment(FrappeTestCase):
def test_shipment_from_delivery_note(self): def test_shipment_from_delivery_note(self):
delivery_note = create_test_delivery_note() delivery_note = create_test_delivery_note()
delivery_note.submit() delivery_note.submit()

View File

@@ -6,6 +6,7 @@ import unittest
import frappe import frappe
from frappe.permissions import add_user_permission, remove_user_permission from frappe.permissions import add_user_permission, remove_user_permission
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import flt, nowdate, nowtime from frappe.utils import flt, nowdate, nowtime
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -28,7 +29,6 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
create_stock_reconciliation, create_stock_reconciliation,
) )
from erpnext.stock.stock_ledger import NegativeStockError, get_previous_sle from erpnext.stock.stock_ledger import NegativeStockError, get_previous_sle
from erpnext.tests.utils import ERPNextTestCase, change_settings
def get_sle(**args): def get_sle(**args):
@@ -42,7 +42,7 @@ def get_sle(**args):
order by timestamp(posting_date, posting_time) desc, creation desc limit 1"""% condition, order by timestamp(posting_date, posting_time) desc, creation desc limit 1"""% condition,
values, as_dict=1) values, as_dict=1)
class TestStockEntry(ERPNextTestCase): class TestStockEntry(FrappeTestCase):
def tearDown(self): def tearDown(self):
frappe.db.rollback() frappe.db.rollback()
frappe.set_user("Administrator") frappe.set_user("Administrator")

View File

@@ -7,6 +7,7 @@ from uuid import uuid4
import frappe import frappe
from frappe.core.page.permission_manager.permission_manager import reset from frappe.core.page.permission_manager.permission_manager import reset
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, today from frappe.utils import add_days, today
from erpnext.stock.doctype.delivery_note.test_delivery_note import ( from erpnext.stock.doctype.delivery_note.test_delivery_note import (
@@ -24,10 +25,9 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
create_stock_reconciliation, create_stock_reconciliation,
) )
from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.tests.utils import ERPNextTestCase
class TestStockLedgerEntry(ERPNextTestCase): class TestStockLedgerEntry(FrappeTestCase):
def setUp(self): def setUp(self):
items = create_items() items = create_items()
reset('Stock Entry') reset('Stock Entry')

View File

@@ -6,6 +6,7 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, cstr, flt, nowdate, nowtime, random_string from frappe.utils import add_days, cstr, flt, nowdate, nowtime, random_string
from erpnext.accounts.utils import get_stock_and_account_balance from erpnext.accounts.utils import get_stock_and_account_balance
@@ -19,10 +20,9 @@ from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
from erpnext.tests.utils import ERPNextTestCase, change_settings
class TestStockReconciliation(ERPNextTestCase): class TestStockReconciliation(FrappeTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
create_batch_or_serial_no_items() create_batch_or_serial_no_items()

View File

@@ -4,11 +4,10 @@
import unittest import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.tests.utils import ERPNextTestCase
class TestStockSettings(ERPNextTestCase): class TestStockSettings(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
frappe.db.set_value("Stock Settings", None, "clean_description_html", 0) frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)

View File

@@ -3,17 +3,17 @@
import frappe import frappe
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from frappe.utils import cint from frappe.utils import cint
import erpnext import erpnext
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.tests.utils import ERPNextTestCase
test_records = frappe.get_test_records('Warehouse') test_records = frappe.get_test_records('Warehouse')
class TestWarehouse(ERPNextTestCase): class TestWarehouse(FrappeTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
if not frappe.get_value('Item', '_Test Item'): if not frappe.get_value('Item', '_Test Item'):

View File

@@ -244,7 +244,7 @@
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2021-12-03 04:40:06.414630", "modified": "2022-03-01 02:37:48.034944",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Warehouse", "name": "Warehouse",
@@ -301,5 +301,7 @@
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "warehouse_name" "states": [],
"title_field": "warehouse_name",
"track_changes": 1
} }

View File

@@ -2,12 +2,12 @@
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_report_data from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_report_data
from erpnext.tests.utils import ERPNextTestCase
class TestStockAgeing(ERPNextTestCase): class TestStockAgeing(FrappeTestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.filters = frappe._dict( self.filters = frappe._dict(
company="_Test Company", company="_Test Company",
@@ -610,4 +610,4 @@ def generate_item_and_item_wh_wise_slots(filters, sle):
item_wh_wise_slots = FIFOSlots(filters, sle).generate() item_wh_wise_slots = FIFOSlots(filters, sle).generate()
filters.show_warehouse_wise_stock = False filters.show_warehouse_wise_stock = False
return item_wise_slots, item_wh_wise_slots return item_wise_slots, item_wh_wise_slots

View File

@@ -1,14 +1,13 @@
import datetime import datetime
import unittest
from frappe import _dict from frappe import _dict
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
from erpnext.tests.utils import ERPNextTestCase
class TestStockAnalyticsReport(ERPNextTestCase): class TestStockAnalyticsReport(FrappeTestCase):
def test_get_period_date_ranges(self): def test_get_period_date_ranges(self):
filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06") filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")

View File

@@ -2,13 +2,13 @@ import json
import unittest import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from hypothesis import given from hypothesis import given
from hypothesis import strategies as st from hypothesis import strategies as st
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.valuation import FIFOValuation, LIFOValuation, round_off_if_near_zero from erpnext.stock.valuation import FIFOValuation, LIFOValuation, round_off_if_near_zero
from erpnext.tests.utils import ERPNextTestCase
qty_gen = st.floats(min_value=-1e6, max_value=1e6) qty_gen = st.floats(min_value=-1e6, max_value=1e6)
value_gen = st.floats(min_value=1, max_value=1e6) value_gen = st.floats(min_value=1, max_value=1e6)
@@ -290,7 +290,7 @@ class TestLIFOValuation(unittest.TestCase):
self.assertTotalQty(total_qty) self.assertTotalQty(total_qty)
self.assertTotalValue(total_value) self.assertTotalValue(total_value)
class TestLIFOValuationSLE(ERPNextTestCase): class TestLIFOValuationSLE(FrappeTestCase):
ITEM_CODE = "_Test LIFO item" ITEM_CODE = "_Test LIFO item"
WAREHOUSE = "_Test Warehouse - _TC" WAREHOUSE = "_Test Warehouse - _TC"

View File

@@ -1,27 +1 @@
{% set domains = frappe.get_doc("Domain Settings").active_domains %} <a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext</a>
{% set links = {
'Manufacturing': '/manufacturing',
'Services': '/services',
'Retail': '/retail',
'Distribution': '/distribution',
'Non Profit': '/non-profit',
'Education': '/education',
'Healthcare': '/healthcare',
'Agriculture': '/agriculture',
'Hospitality': ''
} %}
{% set link = '' %}
{% set label = '' %}
{% if domains %}
{% set label = domains[0].domain %}
{% set link = links[label] %}
{% endif %}
{% if label == "Services" %}
{% set label = "Service" %}
{% endif %}
<a href="https://erpnext.com{{ link }}?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + label + ' Companies') if domains else '' }}</a>

View File

@@ -25,7 +25,7 @@ class TestPointOfSale(unittest.TestCase):
Test Stock and Service Item Search. Test Stock and Service Item Search.
""" """
pos_profile = make_pos_profile() pos_profile = make_pos_profile(name="Test POS Profile for Search")
item1 = make_item("Test Search Stock Item", {"is_stock_item": 1}) item1 = make_item("Test Search Stock Item", {"is_stock_item": 1})
make_stock_entry( make_stock_entry(
item_code="Test Search Stock Item", item_code="Test Search Stock Item",

View File

@@ -1,10 +1,6 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import copy
import signal
import unittest
from contextlib import contextmanager
from typing import Any, Dict, NewType, Optional from typing import Any, Dict, NewType, Optional
import frappe import frappe
@@ -13,22 +9,6 @@ from frappe.core.doctype.report.report import get_report_module_dotted_path
ReportFilters = Dict[str, Any] ReportFilters = Dict[str, Any]
ReportName = NewType("ReportName", str) ReportName = NewType("ReportName", str)
class ERPNextTestCase(unittest.TestCase):
"""A sane default test class for ERPNext tests."""
@classmethod
def setUpClass(cls) -> None:
frappe.db.commit()
return super().setUpClass()
@classmethod
def tearDownClass(cls) -> None:
frappe.db.rollback()
return super().tearDownClass()
def create_test_contact_and_address(): def create_test_contact_and_address():
frappe.db.sql('delete from tabContact') frappe.db.sql('delete from tabContact')
frappe.db.sql('delete from `tabContact Email`') frappe.db.sql('delete from `tabContact Email`')
@@ -81,43 +61,6 @@ def create_test_contact_and_address():
contact_two.insert() contact_two.insert()
@contextmanager
def change_settings(doctype, settings_dict):
""" A context manager to ensure that settings are changed before running
function and restored after running it regardless of exceptions occured.
This is useful in tests where you want to make changes in a function but
don't retain those changes.
import and use as decorator to cover full function or using `with` statement.
example:
@change_settings("Stock Settings", {"item_naming_by": "Naming Series"})
def test_case(self):
...
"""
try:
settings = frappe.get_doc(doctype)
# remember setting
previous_settings = copy.deepcopy(settings_dict)
for key in previous_settings:
previous_settings[key] = getattr(settings, key)
# change setting
for key, value in settings_dict.items():
setattr(settings, key, value)
settings.save()
# singles are cached by default, clear to avoid flake
frappe.db.value_cache[settings] = {}
yield # yield control to calling function
finally:
# restore settings
settings = frappe.get_doc(doctype)
for key, value in previous_settings.items():
setattr(settings, key, value)
settings.save()
def execute_script_report( def execute_script_report(
report_name: ReportName, report_name: ReportName,
module: str, module: str,
@@ -157,24 +100,3 @@ def execute_script_report(
except Exception: except Exception:
print(f"Report failed to execute with filters: {test_filter}") print(f"Report failed to execute with filters: {test_filter}")
raise raise
def timeout(seconds=30, error_message="Test timed out."):
""" Timeout decorator to ensure a test doesn't run for too long.
adapted from https://stackoverflow.com/a/2282656"""
def decorator(func):
def _handle_timeout(signum, frame):
raise Exception(error_message)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wrapper
return decorator